Alerting: use 'Grafana IRM' wording in alerting contact point (#102014)

* chore: use 'Grafana IRM' wording in alerting contact point

* revert temp condition change

* remove unneeded ts assertion

* more renaming

* use translations

* update test

* running make i18n-extract

* avoid "simple" word in copy
This commit is contained in:
Dominik Broj
2025-03-17 23:38:44 +02:00
committed by GitHub
parent 8297c0f34e
commit f7e0f095a2
7 changed files with 90 additions and 41 deletions
@@ -425,9 +425,9 @@ func GetAvailableNotifiers() []*NotifierPlugin {
},
{
Type: "oncall",
Name: "Grafana OnCall",
Description: "Sends alerts to Grafana OnCall",
Heading: "Grafana OnCall settings",
Name: "Grafana IRM",
Description: "Sends alerts to Grafana IRM",
Heading: "Grafana IRM settings",
Options: []NotifierOption{
{
Label: "URL",
@@ -5,7 +5,7 @@ import { useCallback, useEffect, useMemo, useState } from 'react';
import { Controller, FieldErrors, FieldValues, useFormContext } from 'react-hook-form';
import { GrafanaTheme2, SelectableValue } from '@grafana/data';
import { Alert, Button, Field, Select, Text, useStyles2 } from '@grafana/ui';
import { Alert, Button, Field, Select, Stack, Text, useStyles2 } from '@grafana/ui';
import { Trans, t } from 'app/core/internationalization';
import { useUnifiedAlertingSelector } from '../../../hooks/useUnifiedAlertingSelector';
@@ -106,11 +106,16 @@ export function ChannelSubForm<R extends ChannelValues>({
sortBy(notifiers, ({ dto, meta }) => [meta?.order ?? 0, dto.name])
// .notifiers.sort((a, b) => a.dto.name.localeCompare(b.dto.name))
.map<SelectableValue>(({ dto: { name, type }, meta }) => ({
label: name,
// @ts-expect-error ReactNode is supported
label: (
<Stack alignItems="center" gap={1}>
{name}
{meta?.badge}
</Stack>
),
value: type,
description: meta?.description,
isDisabled: meta ? !meta.enabled : false,
imgUrl: meta?.iconUrl,
})),
[notifiers]
);
@@ -42,9 +42,9 @@ const ui = {
onCallIntegrationType: byRole('radiogroup'),
integrationOption: {
new: byRole('radio', {
name: 'A new OnCall integration without escalation chains will be automatically created',
name: 'A new IRM integration without escalation chains will be automatically created',
}),
existing: byRole('radio', { name: 'Use an existing OnCall integration' }),
existing: byRole('radio', { name: 'Use an existing IRM integration' }),
},
newOnCallIntegrationName: byRole('textbox', { name: /Integration name/ }),
existingOnCallIntegrationSelect: (index: number) => byTestId(`items.${index}.settings.url`),
@@ -1,10 +1,12 @@
import { ReactElement } from 'react';
import { NotifierDTO } from '../../../../../../types';
export interface NotifierMetadata {
enabled: boolean;
order: number;
description?: string;
iconUrl?: string;
badge?: ReactElement;
}
export interface Notifier {
@@ -70,16 +70,16 @@ describe('useOnCallIntegration', () => {
const { onCallFormValidators } = result.current;
const gfValidationResult = await waitFor(() => onCallFormValidators.integration_name('grafana-integration'));
expect(gfValidationResult).toBe('Integration of this name already exists in OnCall');
expect(gfValidationResult).toBe('Integration of this name already exists in IRM');
const amValidationResult = await waitFor(() => onCallFormValidators.integration_name('alertmanager-integration'));
expect(amValidationResult).toBe('Integration of this name already exists in OnCall');
expect(amValidationResult).toBe('Integration of this name already exists in IRM');
// ULR validator should check if the provided URL already exists
expect(onCallFormValidators.url('https://oncall.com/grafana-integration')).toBe(true);
expect(onCallFormValidators.url('https://oncall.com/alertmanager-integration')).toBe(
'Selection of existing OnCall integration is required'
'Selection of existing IRM integration is required'
);
});
@@ -3,6 +3,8 @@ import { useCallback, useMemo } from 'react';
import { SelectableValue } from '@grafana/data';
import { isFetchError } from '@grafana/runtime';
import { Badge } from '@grafana/ui';
import { t } from 'app/core/internationalization';
import { getIrmIfPresentOrOnCallPluginId } from 'app/features/alerting/unified/utils/config';
import { useAppNotification } from '../../../../../../../core/copy/appNotification';
@@ -11,7 +13,6 @@ import { NotifierDTO } from '../../../../../../../types';
import { ONCALL_INTEGRATION_V2_FEATURE, onCallApi } from '../../../../api/onCallApi';
import { usePluginBridge } from '../../../../hooks/usePluginBridge';
import { option } from '../../../../utils/notifier-types';
import { GRAFANA_APP_RECEIVERS_SOURCE_IMAGE } from '../types';
import { GRAFANA_ONCALL_INTEGRATION_TYPE, ReceiverTypes } from './onCall';
@@ -100,10 +101,18 @@ export function useOnCallIntegration() {
return true;
} catch (error) {
if (isFetchError(error) && error.status === 409) {
return 'Integration of this name already exists in OnCall';
return t(
'alerting.irm-integration.integration-name-exists',
'Integration of this name already exists in IRM'
);
}
notifyApp.error('Failed to validate OnCall integration name. Is the OnCall API available?');
notifyApp.error(
t(
'alerting.irm-integration.validation-failed',
'Failed to validate IRM integration name. Is the OnCall API available?'
)
);
throw error;
}
},
@@ -114,7 +123,7 @@ export function useOnCallIntegration() {
return grafanaOnCallIntegrations.map((i) => i.integration_url).includes(value)
? true
: 'Selection of existing OnCall integration is required';
: t('alerting.irm-integration.integration-required', 'Selection of existing IRM integration is required');
},
};
}, [grafanaOnCallIntegrations, validateIntegrationNameQuery, isAlertingV2IntegrationEnabled, notifyApp]);
@@ -187,41 +196,57 @@ export function useOnCallIntegration() {
const newIntegrationOption: SelectableValue<string> = {
value: OnCallIntegrationType.NewIntegration,
label: 'New OnCall integration',
description: 'A new OnCall integration without escalation chains will be automatically created',
label: t('alerting.irm-integration.new-integration', 'New IRM integration'),
description: t(
'alerting.irm-integration.new-integration-description',
'A new IRM integration without escalation chains will be automatically created'
),
};
const existingIntegrationOption: SelectableValue<string> = {
value: OnCallIntegrationType.ExistingIntegration,
label: 'Existing OnCall integration',
description: 'Use an existing OnCall integration',
label: t('alerting.irm-integration.existing-integration', 'Existing IRM integration'),
description: t(
'alerting.irm-integration.existing-integration-description',
'Use an existing IRM integration'
),
};
options.unshift(
option(OnCallIntegrationSetting.IntegrationType, 'How to connect to OnCall', '', {
required: true,
element: 'radio',
defaultValue: newIntegrationOption,
selectOptions: [newIntegrationOption, existingIntegrationOption],
}),
option(
OnCallIntegrationSetting.IntegrationType,
t('alerting.irm-integration.connection-method', 'How to connect to IRM'),
'',
{
required: true,
element: 'radio',
defaultValue: newIntegrationOption,
selectOptions: [newIntegrationOption, existingIntegrationOption],
}
),
option(
OnCallIntegrationSetting.IntegrationName,
'Integration name',
'The name of the new OnCall integration',
t('alerting.irm-integration.integration-name', 'Integration name'),
t('alerting.irm-integration.integration-name-description', 'The name of the new IRM integration'),
{
required: true,
showWhen: { field: 'integration_type', is: OnCallIntegrationType.NewIntegration },
}
),
option('url', 'OnCall Integration', 'The OnCall integration to send alerts to', {
element: 'select',
required: true,
showWhen: { field: 'integration_type', is: OnCallIntegrationType.ExistingIntegration },
selectOptions: grafanaOnCallIntegrations.map((i) => ({
label: i.display_name,
description: i.integration_url,
value: i.integration_url,
})),
})
option(
'url',
t('alerting.irm-integration.integration', 'IRM Integration'),
t('alerting.irm-integration.integration-description', 'The IRM integration to send alerts to'),
{
element: 'select',
required: true,
showWhen: { field: 'integration_type', is: OnCallIntegrationType.ExistingIntegration },
selectOptions: grafanaOnCallIntegrations.map((i) => ({
label: i.display_name,
description: i.integration_url,
value: i.integration_url,
})),
}
)
);
return { ...notifier, options };
@@ -238,9 +263,9 @@ export function useOnCallIntegration() {
enabled: !!isOnCallEnabled,
order: -1, // The default is 0. We want OnCall to be the first on the list
description: isOnCallEnabled
? 'Connect effortlessly to Grafana OnCall'
: 'Enable Grafana OnCall plugin to use this integration',
iconUrl: GRAFANA_APP_RECEIVERS_SOURCE_IMAGE[getIrmIfPresentOrOnCallPluginId()],
? t('alerting.irm-integration.enabled-description', 'Seamless way to handle alerts and manage incidents')
: t('alerting.irm-integration.disabled-description', 'Enable Grafana IRM to use this integration'),
badge: <Badge color="blue" text={t('alerting.irm-integration.recommended', 'Recommended')} />,
},
extendOnCallNotifierFeatures,
extendOnCallReceivers,
+17
View File
@@ -376,6 +376,23 @@
"export": "Export",
"reorder": "Re-order rules"
},
"irm-integration": {
"connection-method": "How to connect to IRM",
"disabled-description": "Enable Grafana IRM to use this integration",
"enabled-description": "Seamless way to handle alerts and manage incidents",
"existing-integration": "Existing IRM integration",
"existing-integration-description": "Use an existing IRM integration",
"integration": "IRM Integration",
"integration-description": "The IRM integration to send alerts to",
"integration-name": "Integration name",
"integration-name-description": "The name of the new IRM integration",
"integration-name-exists": "Integration of this name already exists in IRM",
"integration-required": "Selection of existing IRM integration is required",
"new-integration": "New IRM integration",
"new-integration-description": "A new IRM integration without escalation chains will be automatically created",
"recommended": "Recommended",
"validation-failed": "Failed to validate IRM integration name. Is the OnCall API available?"
},
"list-view": {
"empty": {
"new-alert-rule": "New alert rule",