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:
Ildar Iskhakov 2023-05-31 19:26:36 +08:00 committed by GitHub
parent f299fb9814
commit 5975b9dd8c
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
11 changed files with 97 additions and 27 deletions

View file

@ -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...

View file

@ -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"]:

View file

@ -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

View file

@ -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

View file

@ -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>

View file

@ -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

View file

@ -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}

View file

@ -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;
}

View file

@ -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();

View file

@ -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 && (

View file

@ -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>