Change integrations page wording and add more guidance (#1986)
# What this PR does ## Which issue(s) this PR fixes ## Checklist - [ ] Unit, integration, and e2e (if applicable) tests updated - [ ] Documentation added (or `pr:no public docs` PR label added if not required) - [ ] `CHANGELOG.md` updated (or `pr:no changelog` PR label added if not required) --------- Co-authored-by: Yulia Shanyrova <yulia.shanyrova@grafana.com>
This commit is contained in:
parent
f299fb9814
commit
5975b9dd8c
11 changed files with 97 additions and 27 deletions
|
|
@ -49,6 +49,11 @@ class IntegrationOptionsMixin:
|
|||
integration_config.slug: integration_config.short_description for integration_config in _config
|
||||
}
|
||||
INTEGRATION_FEATURED = [integration_config.slug for integration_config in _config if integration_config.is_featured]
|
||||
INTEGRATION_FEATURED_TAG_NAME = {
|
||||
integration_config.slug: integration_config.featured_tag_name
|
||||
for integration_config in _config
|
||||
if hasattr(integration_config, "featured_tag_name")
|
||||
}
|
||||
|
||||
# The following attributes dynamically generated and used by apps.alerts.incident_appearance.renderers, templaters
|
||||
# e.g. INTEGRATION_TO_DEFAULT_SLACK_TITLE_TEMPLATE, INTEGRATION_TO_DEFAULT_SLACK_MESSAGE_TEMPLATE, etc...
|
||||
|
|
|
|||
|
|
@ -198,6 +198,9 @@ class AlertReceiveChannelView(
|
|||
"display_name": integration_title,
|
||||
"short_description": AlertReceiveChannel.INTEGRATION_SHORT_DESCRIPTION[integration_id],
|
||||
"featured": integration_id in AlertReceiveChannel.INTEGRATION_FEATURED,
|
||||
"featured_tag_name": AlertReceiveChannel.INTEGRATION_FEATURED_TAG_NAME[integration_id]
|
||||
if integration_id in AlertReceiveChannel.INTEGRATION_FEATURED_TAG_NAME
|
||||
else None,
|
||||
}
|
||||
# if integration is featured we show it in the beginning
|
||||
if choice["featured"]:
|
||||
|
|
|
|||
|
|
@ -8,6 +8,7 @@ short_description = (
|
|||
description = None
|
||||
is_displayed_on_web = True
|
||||
is_featured = True
|
||||
featured_tag_name = "Quick Connect"
|
||||
is_able_to_autoresolve = True
|
||||
is_demo_alert_enabled = True
|
||||
|
||||
|
|
|
|||
|
|
@ -2,9 +2,10 @@
|
|||
enabled = True
|
||||
title = "Webhook"
|
||||
slug = "webhook"
|
||||
short_description = None
|
||||
short_description = "If your monitoring system isn't listed, choose Webhook for generic templates, and feel free to modify them as needed."
|
||||
description = None
|
||||
is_featured = False
|
||||
is_featured = True
|
||||
featured_tag_name = "Generic"
|
||||
is_displayed_on_web = True
|
||||
is_able_to_autoresolve = True
|
||||
is_demo_alert_enabled = True
|
||||
|
|
|
|||
|
|
@ -55,7 +55,10 @@ const CollapsedIntegrationRouteDisplay: React.FC<CollapsedIntegrationRouteDispla
|
|||
alertReceiveChannelStore.channelFilterIds[alertReceiveChannelId],
|
||||
routeIndex
|
||||
)}
|
||||
tooltipTitle={undefined}
|
||||
tooltipTitle={IntegrationHelper.getRouteConditionTooltipWording(
|
||||
alertReceiveChannelStore.channelFilterIds[alertReceiveChannelId],
|
||||
routeIndex
|
||||
)}
|
||||
tooltipContent={undefined}
|
||||
/>
|
||||
{routeWording === 'Default' && (
|
||||
|
|
@ -93,7 +96,7 @@ const CollapsedIntegrationRouteDisplay: React.FC<CollapsedIntegrationRouteDispla
|
|||
|
||||
<HorizontalGroup>
|
||||
<Icon name="list-ui-alt" />
|
||||
<Text type="secondary">Escalate to</Text>
|
||||
<Text type="secondary">Trigger escalation chain:</Text>
|
||||
|
||||
{escalationChain?.name && (
|
||||
<PluginLink
|
||||
|
|
@ -112,9 +115,7 @@ const CollapsedIntegrationRouteDisplay: React.FC<CollapsedIntegrationRouteDispla
|
|||
<div className={cx('icon-exclamation')}>
|
||||
<Icon name="exclamation-triangle" />
|
||||
</div>
|
||||
<Text type="primary" strong>
|
||||
No Escalation chain
|
||||
</Text>
|
||||
<Text type="primary">No Escalation chain selected</Text>
|
||||
</HorizontalGroup>
|
||||
)}
|
||||
</HorizontalGroup>
|
||||
|
|
|
|||
|
|
@ -113,7 +113,7 @@ const ExpandedIntegrationRouteDisplay: React.FC<ExpandedIntegrationRouteDisplayP
|
|||
<TooltipBadge
|
||||
borderType="success"
|
||||
text={IntegrationHelper.getRouteConditionWording(channelFilterIds, routeIndex)}
|
||||
tooltipTitle={undefined}
|
||||
tooltipTitle={IntegrationHelper.getRouteConditionTooltipWording(channelFilterIds, routeIndex)}
|
||||
tooltipContent={undefined}
|
||||
/>
|
||||
</HorizontalGroup>
|
||||
|
|
@ -129,6 +129,16 @@ const ExpandedIntegrationRouteDisplay: React.FC<ExpandedIntegrationRouteDisplayP
|
|||
}
|
||||
content={
|
||||
<VerticalGroup spacing="xs">
|
||||
{routeIndex !== channelFiltersTotal.length - 1 && (
|
||||
<IntegrationBlockItem>
|
||||
<VerticalGroup>
|
||||
<Text type="secondary">
|
||||
If the Routing Template is True, group the alerts using the Grouping Template, publish them to
|
||||
messengers, and trigger the escalation chain.
|
||||
</Text>
|
||||
</VerticalGroup>
|
||||
</IntegrationBlockItem>
|
||||
)}
|
||||
{/* Show Routing Template only for If/Else Routes, not for Default */}
|
||||
{!isDefault && (
|
||||
<IntegrationBlockItem>
|
||||
|
|
@ -206,9 +216,13 @@ const ExpandedIntegrationRouteDisplay: React.FC<ExpandedIntegrationRouteDisplayP
|
|||
></Select>
|
||||
</WithPermissionControlTooltip>
|
||||
|
||||
<Tooltip content={'Reload escalation chains list'} placement={'top'}>
|
||||
<Button variant={'secondary'} icon={'sync'} size={'md'} onClick={onEscalationChainsRefresh} />
|
||||
</Tooltip>
|
||||
<Button
|
||||
variant={'secondary'}
|
||||
tooltip={'Refresh Escalation Chains'}
|
||||
icon={'sync'}
|
||||
size={'md'}
|
||||
onClick={onEscalationChainsRefresh}
|
||||
/>
|
||||
|
||||
<PluginLink className={cx('hover-button')} target="_blank" query={escalationChainRedirectObj}>
|
||||
<Tooltip
|
||||
|
|
|
|||
|
|
@ -100,6 +100,10 @@ const IntegrationForm2 = observer((props: IntegrationFormProps) => {
|
|||
<Drawer scrollableContent title="New Integration" onClose={onHide} closeOnMaskClick={false} width="640px">
|
||||
<div className={cx('content')}>
|
||||
<VerticalGroup>
|
||||
<Text type="secondary">
|
||||
Integration receives alerts on an unique API URL, interprets them using set of templates tailored for
|
||||
monitoring system and starts escalations.
|
||||
</Text>
|
||||
<div className={cx('search-integration')}>
|
||||
<Input
|
||||
autoFocus
|
||||
|
|
@ -129,7 +133,9 @@ const IntegrationForm2 = observer((props: IntegrationFormProps) => {
|
|||
<Text strong data-testid="integration-display-name">
|
||||
{alertReceiveChannelChoice.display_name}
|
||||
</Text>
|
||||
{alertReceiveChannelChoice.featured && <Tag name="Quick connect" colorIndex={5} />}
|
||||
{alertReceiveChannelChoice.featured && alertReceiveChannelChoice.featured_tag_name && (
|
||||
<Tag name={alertReceiveChannelChoice.featured_tag_name} colorIndex={5} />
|
||||
)}
|
||||
</HorizontalGroup>
|
||||
<Text type="secondary" size="small">
|
||||
{alertReceiveChannelChoice.short_description}
|
||||
|
|
|
|||
|
|
@ -13,6 +13,7 @@ export interface AlertReceiveChannelOption {
|
|||
value: number;
|
||||
featured: boolean;
|
||||
short_description: string;
|
||||
featured_tag_name: string;
|
||||
}
|
||||
|
||||
export interface AlertReceiveChannelCounters {
|
||||
|
|
@ -43,6 +44,7 @@ export interface AlertReceiveChannel {
|
|||
heartbeat: Heartbeat | null;
|
||||
is_available_for_integration_heartbeat: boolean;
|
||||
routes_count: number;
|
||||
connected_escalations_chains_count: number;
|
||||
allow_delete: boolean;
|
||||
deleted?: boolean;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -46,6 +46,15 @@ const IntegrationHelper = {
|
|||
return routeIndex ? 'Else' : 'If';
|
||||
},
|
||||
|
||||
getRouteConditionTooltipWording(channelFilters: Array<ChannelFilter['id']>, routeIndex: number) {
|
||||
const totalCount = Object.keys(channelFilters).length;
|
||||
|
||||
if (routeIndex === totalCount - 1) {
|
||||
return 'If the alert payload does not match to the previous routes, it will be directed to this default route.';
|
||||
}
|
||||
return 'If the alert payload evaluates the route template as True, it will be directed to this route. It will not be evaluated against the subsequent routes.';
|
||||
},
|
||||
|
||||
getMaintenanceText(maintenanceUntill: number, mode: number = undefined) {
|
||||
const date = dayjs(new Date(maintenanceUntill * 1000));
|
||||
const now = dayjs();
|
||||
|
|
|
|||
|
|
@ -207,7 +207,6 @@ class Integration2 extends React.Component<Integration2Props, Integration2State>
|
|||
<IntegrationHeader
|
||||
alertReceiveChannel={alertReceiveChannel}
|
||||
alertReceiveChannelCounter={alertReceiveChannelCounter}
|
||||
channelFilterIds={channelFilterIds}
|
||||
integration={integration}
|
||||
/>
|
||||
</div>
|
||||
|
|
@ -255,7 +254,10 @@ class Integration2 extends React.Component<Integration2Props, Integration2State>
|
|||
|
||||
<div className={cx('templates__content')}>
|
||||
<div className={cx('templates__container')}>
|
||||
<div className={cx('templates__item', 'templates__item--large')}>
|
||||
<div
|
||||
className={cx('templates__item', 'templates__item--large')}
|
||||
onClick={() => this.setState({ isTemplateSettingsOpen: true })}
|
||||
>
|
||||
<Text type="secondary" className={cx('templates__item-text')}>
|
||||
Grouping:
|
||||
</Text>
|
||||
|
|
@ -264,7 +266,10 @@ class Integration2 extends React.Component<Integration2Props, Integration2State>
|
|||
</Text>
|
||||
</div>
|
||||
|
||||
<div className={cx('templates__item', 'templates__item--large')}>
|
||||
<div
|
||||
className={cx('templates__item', 'templates__item--large')}
|
||||
onClick={() => this.setState({ isTemplateSettingsOpen: true })}
|
||||
>
|
||||
<Text type="secondary" className={cx('templates__item-text')}>
|
||||
Autoresolve:
|
||||
</Text>
|
||||
|
|
@ -273,7 +278,10 @@ class Integration2 extends React.Component<Integration2Props, Integration2State>
|
|||
</Text>
|
||||
</div>
|
||||
|
||||
<div className={cx('templates__item', 'templates__item--small')}>
|
||||
<div
|
||||
className={cx('templates__item', 'templates__item--small')}
|
||||
onClick={() => this.setState({ isTemplateSettingsOpen: true })}
|
||||
>
|
||||
<Text type="secondary" className={cx('templates__item-text')}>
|
||||
Visualisation:
|
||||
</Text>
|
||||
|
|
@ -968,14 +976,12 @@ interface IntegrationHeaderProps {
|
|||
alertReceiveChannelCounter: AlertReceiveChannelCounters;
|
||||
alertReceiveChannel: AlertReceiveChannel;
|
||||
integration: SelectOption;
|
||||
channelFilterIds: string[];
|
||||
}
|
||||
|
||||
const IntegrationHeader: React.FC<IntegrationHeaderProps> = ({
|
||||
integration,
|
||||
alertReceiveChannelCounter,
|
||||
alertReceiveChannel,
|
||||
channelFilterIds,
|
||||
}) => {
|
||||
const { grafanaTeamStore, heartbeatStore, alertReceiveChannelStore } = useStore();
|
||||
|
||||
|
|
@ -989,8 +995,8 @@ const IntegrationHeader: React.FC<IntegrationHeaderProps> = ({
|
|||
>
|
||||
<TooltipBadge
|
||||
borderType="primary"
|
||||
tooltipTitle={getAlertReceiveChannelCounterTooltip()}
|
||||
tooltipContent={undefined}
|
||||
tooltipTitle={undefined}
|
||||
tooltipContent={getAlertReceiveChannelCounterTooltip()}
|
||||
text={alertReceiveChannelCounter?.alerts_count + '/' + alertReceiveChannelCounter?.alert_groups_count}
|
||||
/>
|
||||
</PluginLink>
|
||||
|
|
@ -999,9 +1005,17 @@ const IntegrationHeader: React.FC<IntegrationHeaderProps> = ({
|
|||
<TooltipBadge
|
||||
borderType="success"
|
||||
icon="link"
|
||||
text={channelFilterIds.length}
|
||||
tooltipTitle={`${channelFilterIds.length} Routes`}
|
||||
tooltipContent={undefined}
|
||||
text={`${alertReceiveChannel.connected_escalations_chains_count}/${alertReceiveChannel.routes_count}`}
|
||||
tooltipTitle=""
|
||||
tooltipContent={
|
||||
alertReceiveChannel.connected_escalations_chains_count +
|
||||
' connected escalation chain' +
|
||||
(alertReceiveChannel.connected_escalations_chains_count === 1 ? '' : 's') +
|
||||
' in ' +
|
||||
alertReceiveChannel.routes_count +
|
||||
' route' +
|
||||
(alertReceiveChannel.routes_count === 1 ? '' : 's')
|
||||
}
|
||||
/>
|
||||
|
||||
{alertReceiveChannel.maintenance_till && (
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
import React from 'react';
|
||||
|
||||
import { HorizontalGroup, Button, IconButton } from '@grafana/ui';
|
||||
import { HorizontalGroup, Button, IconButton, VerticalGroup } from '@grafana/ui';
|
||||
import cn from 'classnames/bind';
|
||||
import { debounce } from 'lodash-es';
|
||||
import { observer } from 'mobx-react';
|
||||
|
|
@ -165,7 +165,12 @@ class Integrations extends React.Component<IntegrationsProps, IntegrationsState>
|
|||
<div className={cx('root')}>
|
||||
<div className={cx('title')}>
|
||||
<HorizontalGroup justify="space-between">
|
||||
<Text.Title level={3}>Integrations 2</Text.Title>
|
||||
<VerticalGroup>
|
||||
<Text.Title level={3}>Integrations 2</Text.Title>
|
||||
<Text type="secondary">
|
||||
Receive alerts, group and interpret using templates and route to escalations
|
||||
</Text>
|
||||
</VerticalGroup>
|
||||
<WithPermissionControlTooltip userAction={UserActions.IntegrationsWrite}>
|
||||
<Button
|
||||
onClick={() => {
|
||||
|
|
@ -257,6 +262,7 @@ class Integrations extends React.Component<IntegrationsProps, IntegrationsState>
|
|||
renderIntegrationStatus(item: AlertReceiveChannel, alertReceiveChannelStore) {
|
||||
const alertReceiveChannelCounter = alertReceiveChannelStore.counters[item.id];
|
||||
let routesCounter = item.routes_count;
|
||||
let connectedEscalationsChainsCount = item.connected_escalations_chains_count;
|
||||
|
||||
return (
|
||||
<HorizontalGroup spacing="xs">
|
||||
|
|
@ -282,9 +288,17 @@ class Integrations extends React.Component<IntegrationsProps, IntegrationsState>
|
|||
<TooltipBadge
|
||||
borderType="success"
|
||||
icon="link"
|
||||
text={routesCounter}
|
||||
text={`${connectedEscalationsChainsCount}/${routesCounter}`}
|
||||
tooltipTitle=""
|
||||
tooltipContent={`${routesCounter} routes`}
|
||||
tooltipContent={
|
||||
connectedEscalationsChainsCount +
|
||||
' connected escalation chain' +
|
||||
(connectedEscalationsChainsCount === 1 ? '' : 's') +
|
||||
' in ' +
|
||||
routesCounter +
|
||||
' route' +
|
||||
(routesCounter === 1 ? '' : 's')
|
||||
}
|
||||
/>
|
||||
)}
|
||||
</HorizontalGroup>
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue