mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
Alerting: improve provisioning indicator for notification policies (#71160)
This commit is contained in:
@@ -16,7 +16,6 @@ import { alertmanagerApi } from './api/alertmanagerApi';
|
|||||||
import { useGetContactPointsState } from './api/receiversApi';
|
import { useGetContactPointsState } from './api/receiversApi';
|
||||||
import { AlertmanagerPageWrapper } from './components/AlertingPageWrapper';
|
import { AlertmanagerPageWrapper } from './components/AlertingPageWrapper';
|
||||||
import { GrafanaAlertmanagerDeliveryWarning } from './components/GrafanaAlertmanagerDeliveryWarning';
|
import { GrafanaAlertmanagerDeliveryWarning } from './components/GrafanaAlertmanagerDeliveryWarning';
|
||||||
import { ProvisionedResource, ProvisioningAlert } from './components/Provisioning';
|
|
||||||
import { MuteTimingsTable } from './components/mute-timings/MuteTimingsTable';
|
import { MuteTimingsTable } from './components/mute-timings/MuteTimingsTable';
|
||||||
import {
|
import {
|
||||||
computeInheritedTree,
|
computeInheritedTree,
|
||||||
@@ -183,7 +182,7 @@ const AmRoutes = () => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const vanillaPrometheusAlertManager = isVanillaPrometheusAlertManagerDataSource(selectedAlertmanager);
|
const vanillaPrometheusAlertManager = isVanillaPrometheusAlertManagerDataSource(selectedAlertmanager);
|
||||||
const readOnlyPolicies = vanillaPrometheusAlertManager || isProvisioned;
|
const readOnlyPolicies = vanillaPrometheusAlertManager;
|
||||||
const readOnlyMuteTimings = vanillaPrometheusAlertManager;
|
const readOnlyMuteTimings = vanillaPrometheusAlertManager;
|
||||||
|
|
||||||
const numberOfMuteTimings = result?.alertmanager_config.mute_time_intervals?.length ?? 0;
|
const numberOfMuteTimings = result?.alertmanager_config.mute_time_intervals?.length ?? 0;
|
||||||
@@ -227,7 +226,6 @@ const AmRoutes = () => {
|
|||||||
{policyTreeTabActive && (
|
{policyTreeTabActive && (
|
||||||
<>
|
<>
|
||||||
<GrafanaAlertmanagerDeliveryWarning currentAlertmanager={selectedAlertmanager} />
|
<GrafanaAlertmanagerDeliveryWarning currentAlertmanager={selectedAlertmanager} />
|
||||||
{isProvisioned && <ProvisioningAlert resource={ProvisionedResource.RootNotificationPolicy} />}
|
|
||||||
<Stack direction="column" gap={1}>
|
<Stack direction="column" gap={1}>
|
||||||
{rootRoute && (
|
{rootRoute && (
|
||||||
<NotificationPoliciesFilter
|
<NotificationPoliciesFilter
|
||||||
@@ -244,6 +242,7 @@ const AmRoutes = () => {
|
|||||||
alertGroups={alertGroups ?? []}
|
alertGroups={alertGroups ?? []}
|
||||||
contactPointsState={contactPointsState.receivers}
|
contactPointsState={contactPointsState.receivers}
|
||||||
readOnly={readOnlyPolicies}
|
readOnly={readOnlyPolicies}
|
||||||
|
provisioned={isProvisioned}
|
||||||
alertManagerSourceName={selectedAlertmanager}
|
alertManagerSourceName={selectedAlertmanager}
|
||||||
onAddPolicy={openAddModal}
|
onAddPolicy={openAddModal}
|
||||||
onEditPolicy={openEditModal}
|
onEditPolicy={openEditModal}
|
||||||
|
|||||||
@@ -3,13 +3,14 @@ import React from 'react';
|
|||||||
|
|
||||||
import { GrafanaTheme2 } from '@grafana/data';
|
import { GrafanaTheme2 } from '@grafana/data';
|
||||||
import { Stack } from '@grafana/experimental';
|
import { Stack } from '@grafana/experimental';
|
||||||
import { Badge, Button, Dropdown, Icon, Menu, Tooltip, useStyles2 } from '@grafana/ui';
|
import { Button, Dropdown, Icon, Menu, Tooltip, useStyles2 } from '@grafana/ui';
|
||||||
import { Span } from '@grafana/ui/src/unstable';
|
import { Span } from '@grafana/ui/src/unstable';
|
||||||
import ConditionalWrap from 'app/features/alerting/components/ConditionalWrap';
|
import ConditionalWrap from 'app/features/alerting/components/ConditionalWrap';
|
||||||
import { GrafanaNotifierType } from 'app/types/alerting';
|
import { GrafanaNotifierType } from 'app/types/alerting';
|
||||||
|
|
||||||
import { INTEGRATION_ICONS } from '../../types/contact-points';
|
import { INTEGRATION_ICONS } from '../../types/contact-points';
|
||||||
import { MetaText } from '../MetaText';
|
import { MetaText } from '../MetaText';
|
||||||
|
import { ProvisioningBadge } from '../Provisioning';
|
||||||
import { Spacer } from '../Spacer';
|
import { Spacer } from '../Spacer';
|
||||||
import { Strong } from '../Strong';
|
import { Strong } from '../Strong';
|
||||||
|
|
||||||
@@ -113,7 +114,7 @@ const ContactPointHeader = (props: ContactPointHeaderProps) => {
|
|||||||
) : (
|
) : (
|
||||||
<MetaText>is not used</MetaText>
|
<MetaText>is not used</MetaText>
|
||||||
)}
|
)}
|
||||||
{isProvisioned && <Badge color="purple" text="Provisioned" />}
|
{isProvisioned && <ProvisioningBadge />}
|
||||||
<Spacer />
|
<Spacer />
|
||||||
<ConditionalWrap
|
<ConditionalWrap
|
||||||
shouldWrap={isProvisioned}
|
shouldWrap={isProvisioned}
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ import { Stack } from '@grafana/experimental';
|
|||||||
import { Badge, Button, Dropdown, getTagColorsFromName, Icon, Menu, Tooltip, useStyles2 } from '@grafana/ui';
|
import { Badge, Button, Dropdown, getTagColorsFromName, Icon, Menu, Tooltip, useStyles2 } from '@grafana/ui';
|
||||||
import { Span } from '@grafana/ui/src/unstable';
|
import { Span } from '@grafana/ui/src/unstable';
|
||||||
import { contextSrv } from 'app/core/core';
|
import { contextSrv } from 'app/core/core';
|
||||||
|
import ConditionalWrap from 'app/features/alerting/components/ConditionalWrap';
|
||||||
import { RouteWithID, Receiver, ObjectMatcher, AlertmanagerGroup } from 'app/plugins/datasource/alertmanager/types';
|
import { RouteWithID, Receiver, ObjectMatcher, AlertmanagerGroup } from 'app/plugins/datasource/alertmanager/types';
|
||||||
import { ReceiversState } from 'app/types';
|
import { ReceiversState } from 'app/types';
|
||||||
|
|
||||||
@@ -20,6 +21,7 @@ import { getInheritedProperties, InhertitableProperties } from '../../utils/noti
|
|||||||
import { HoverCard } from '../HoverCard';
|
import { HoverCard } from '../HoverCard';
|
||||||
import { Label } from '../Label';
|
import { Label } from '../Label';
|
||||||
import { MetaText } from '../MetaText';
|
import { MetaText } from '../MetaText';
|
||||||
|
import { ProvisioningBadge } from '../Provisioning';
|
||||||
import { Spacer } from '../Spacer';
|
import { Spacer } from '../Spacer';
|
||||||
import { Strong } from '../Strong';
|
import { Strong } from '../Strong';
|
||||||
|
|
||||||
@@ -31,6 +33,7 @@ interface PolicyComponentProps {
|
|||||||
alertGroups?: AlertmanagerGroup[];
|
alertGroups?: AlertmanagerGroup[];
|
||||||
contactPointsState?: ReceiversState;
|
contactPointsState?: ReceiversState;
|
||||||
readOnly?: boolean;
|
readOnly?: boolean;
|
||||||
|
provisioned?: boolean;
|
||||||
inheritedProperties?: Partial<InhertitableProperties>;
|
inheritedProperties?: Partial<InhertitableProperties>;
|
||||||
routesMatchingFilters?: RouteWithID[];
|
routesMatchingFilters?: RouteWithID[];
|
||||||
// routeAlertGroupsMap?: Map<string, AlertmanagerGroup[]>;
|
// routeAlertGroupsMap?: Map<string, AlertmanagerGroup[]>;
|
||||||
@@ -50,6 +53,7 @@ const Policy: FC<PolicyComponentProps> = ({
|
|||||||
receivers = [],
|
receivers = [],
|
||||||
contactPointsState,
|
contactPointsState,
|
||||||
readOnly = false,
|
readOnly = false,
|
||||||
|
provisioned = false,
|
||||||
alertGroups = [],
|
alertGroups = [],
|
||||||
alertManagerSourceName,
|
alertManagerSourceName,
|
||||||
currentRoute,
|
currentRoute,
|
||||||
@@ -143,49 +147,57 @@ const Policy: FC<PolicyComponentProps> = ({
|
|||||||
<Spacer />
|
<Spacer />
|
||||||
{/* TODO maybe we should move errors to the gutter instead? */}
|
{/* TODO maybe we should move errors to the gutter instead? */}
|
||||||
{errors.length > 0 && <Errors errors={errors} />}
|
{errors.length > 0 && <Errors errors={errors} />}
|
||||||
{!readOnly && (
|
{provisioned && <ProvisioningBadge />}
|
||||||
|
{readOnly ? null : (
|
||||||
<Stack direction="row" gap={0.5}>
|
<Stack direction="row" gap={0.5}>
|
||||||
<Button
|
<ConditionalWrap shouldWrap={provisioned} wrap={ProvisionedTooltip}>
|
||||||
variant="secondary"
|
|
||||||
icon="plus"
|
|
||||||
size="sm"
|
|
||||||
onClick={() => onAddPolicy(currentRoute)}
|
|
||||||
type="button"
|
|
||||||
>
|
|
||||||
New nested policy
|
|
||||||
</Button>
|
|
||||||
<Dropdown
|
|
||||||
overlay={
|
|
||||||
<Menu>
|
|
||||||
<Menu.Item
|
|
||||||
icon="edit"
|
|
||||||
disabled={!isEditable}
|
|
||||||
label="Edit"
|
|
||||||
onClick={() => onEditPolicy(currentRoute, isDefaultPolicy)}
|
|
||||||
/>
|
|
||||||
{isDeletable && (
|
|
||||||
<>
|
|
||||||
<Menu.Divider />
|
|
||||||
<Menu.Item
|
|
||||||
destructive
|
|
||||||
icon="trash-alt"
|
|
||||||
label="Delete"
|
|
||||||
onClick={() => onDeletePolicy(currentRoute)}
|
|
||||||
/>
|
|
||||||
</>
|
|
||||||
)}
|
|
||||||
</Menu>
|
|
||||||
}
|
|
||||||
>
|
|
||||||
<Button
|
<Button
|
||||||
icon="ellipsis-h"
|
|
||||||
variant="secondary"
|
variant="secondary"
|
||||||
|
icon="plus"
|
||||||
size="sm"
|
size="sm"
|
||||||
|
onClick={() => onAddPolicy(currentRoute)}
|
||||||
|
disabled={provisioned}
|
||||||
type="button"
|
type="button"
|
||||||
aria-label="more-actions"
|
>
|
||||||
data-testid="more-actions"
|
New nested policy
|
||||||
/>
|
</Button>
|
||||||
</Dropdown>
|
</ConditionalWrap>
|
||||||
|
|
||||||
|
<ConditionalWrap shouldWrap={provisioned} wrap={ProvisionedTooltip}>
|
||||||
|
<Dropdown
|
||||||
|
overlay={
|
||||||
|
<Menu>
|
||||||
|
<Menu.Item
|
||||||
|
icon="edit"
|
||||||
|
disabled={!isEditable}
|
||||||
|
label="Edit"
|
||||||
|
onClick={() => onEditPolicy(currentRoute, isDefaultPolicy)}
|
||||||
|
/>
|
||||||
|
{isDeletable && (
|
||||||
|
<>
|
||||||
|
<Menu.Divider />
|
||||||
|
<Menu.Item
|
||||||
|
destructive
|
||||||
|
icon="trash-alt"
|
||||||
|
label="Delete"
|
||||||
|
onClick={() => onDeletePolicy(currentRoute)}
|
||||||
|
/>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
</Menu>
|
||||||
|
}
|
||||||
|
>
|
||||||
|
<Button
|
||||||
|
icon="ellipsis-h"
|
||||||
|
variant="secondary"
|
||||||
|
size="sm"
|
||||||
|
type="button"
|
||||||
|
aria-label="more-actions"
|
||||||
|
data-testid="more-actions"
|
||||||
|
disabled={provisioned}
|
||||||
|
/>
|
||||||
|
</Dropdown>
|
||||||
|
</ConditionalWrap>
|
||||||
</Stack>
|
</Stack>
|
||||||
)}
|
)}
|
||||||
</Stack>
|
</Stack>
|
||||||
@@ -270,7 +282,7 @@ const Policy: FC<PolicyComponentProps> = ({
|
|||||||
currentRoute={child}
|
currentRoute={child}
|
||||||
receivers={receivers}
|
receivers={receivers}
|
||||||
contactPointsState={contactPointsState}
|
contactPointsState={contactPointsState}
|
||||||
readOnly={readOnly}
|
readOnly={readOnly || provisioned}
|
||||||
inheritedProperties={childInheritedProperties}
|
inheritedProperties={childInheritedProperties}
|
||||||
onAddPolicy={onAddPolicy}
|
onAddPolicy={onAddPolicy}
|
||||||
onEditPolicy={onEditPolicy}
|
onEditPolicy={onEditPolicy}
|
||||||
@@ -288,6 +300,12 @@ const Policy: FC<PolicyComponentProps> = ({
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const ProvisionedTooltip = (children: ReactNode) => (
|
||||||
|
<Tooltip content="Provisioned items cannot be edited in the UI" placement="top">
|
||||||
|
<span>{children}</span>
|
||||||
|
</Tooltip>
|
||||||
|
);
|
||||||
|
|
||||||
const Errors: FC<{ errors: React.ReactNode[] }> = ({ errors }) => (
|
const Errors: FC<{ errors: React.ReactNode[] }> = ({ errors }) => (
|
||||||
<HoverCard
|
<HoverCard
|
||||||
arrow
|
arrow
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ import { useFormContext } from 'react-hook-form';
|
|||||||
|
|
||||||
import { GrafanaTheme2, SelectableValue } from '@grafana/data';
|
import { GrafanaTheme2, SelectableValue } from '@grafana/data';
|
||||||
import { Stack } from '@grafana/experimental';
|
import { Stack } from '@grafana/experimental';
|
||||||
import { AsyncSelect, Badge, Field, InputControl, Label, useStyles2 } from '@grafana/ui';
|
import { AsyncSelect, Field, InputControl, Label, useStyles2 } from '@grafana/ui';
|
||||||
import { contextSrv } from 'app/core/core';
|
import { contextSrv } from 'app/core/core';
|
||||||
import { AccessControlAction, useDispatch } from 'app/types';
|
import { AccessControlAction, useDispatch } from 'app/types';
|
||||||
import { CombinedRuleGroup } from 'app/types/unified-alerting';
|
import { CombinedRuleGroup } from 'app/types/unified-alerting';
|
||||||
@@ -18,6 +18,7 @@ import { GRAFANA_RULES_SOURCE_NAME } from '../../utils/datasource';
|
|||||||
import { MINUTE } from '../../utils/rule-form';
|
import { MINUTE } from '../../utils/rule-form';
|
||||||
import { isGrafanaRulerRule } from '../../utils/rules';
|
import { isGrafanaRulerRule } from '../../utils/rules';
|
||||||
import { InfoIcon } from '../InfoIcon';
|
import { InfoIcon } from '../InfoIcon';
|
||||||
|
import { ProvisioningBadge } from '../Provisioning';
|
||||||
|
|
||||||
import { Folder, RuleFolderPicker } from './RuleFolderPicker';
|
import { Folder, RuleFolderPicker } from './RuleFolderPicker';
|
||||||
import { checkForPathSeparator } from './util';
|
import { checkForPathSeparator } from './util';
|
||||||
@@ -174,7 +175,7 @@ export function FolderAndGroup() {
|
|||||||
{option.isDisabled && (
|
{option.isDisabled && (
|
||||||
<>
|
<>
|
||||||
{' '}
|
{' '}
|
||||||
<Badge color="purple" text="Provisioned" />
|
<ProvisioningBadge />
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
Reference in New Issue
Block a user