Alerting: Add frontend validation for placeholder email addresses

- Disable test button when placeholder email is detected
- Show info alert guiding users to configure valid email address
- Add tooltip explaining why test is disabled
- Pass channel values to TestContactPointModal for validation
- Proactively prevent testing with placeholder addresses
- Non-email contact points remain unaffected
This commit is contained in:
tonypowa
2025-11-03 17:19:35 +01:00
parent 08f58b2346
commit 7ba340f4a2
3 changed files with 95 additions and 6 deletions
@@ -70,6 +70,17 @@ export function ChannelSubForm<R extends ChannelValues>({
const onCallIntegrationType = watch(`${settingsFieldPath}.integration_type`);
const isTestAvailable = onCallIntegrationType !== OnCallIntegrationType.NewIntegration;
// Check if email integration has placeholder addresses
const emailAddresses = watch(`${settingsFieldPath}.addresses`);
const hasPlaceholderEmail = useMemo(() => {
if (selectedType !== 'email' || !emailAddresses) {
return false;
}
const placeholders = ['<example@email.com>', 'example@email.com'];
const addresses = typeof emailAddresses === 'string' ? [emailAddresses] : emailAddresses;
return addresses.some((addr: string) => placeholders.includes(addr?.trim()));
}, [selectedType, emailAddresses]);
useEffect(() => {
register(`${channelFieldPath}.__id`);
/* Need to manually register secureFields or else they'll
@@ -230,7 +241,22 @@ export function ChannelSubForm<R extends ChannelValues>({
</div>
<div className={styles.buttons}>
{isTestable && onTest && isTestAvailable && (
<Button size="xs" variant="secondary" type="button" onClick={() => handleTest()} icon="message">
<Button
disabled={hasPlaceholderEmail}
size="xs"
variant="secondary"
type="button"
onClick={() => handleTest()}
icon="message"
tooltip={
hasPlaceholderEmail
? t(
'alerting.channel-sub-form.test-disabled-placeholder',
'Please configure a valid email address before testing'
)
: undefined
}
>
<Trans i18nKey="alerting.channel-sub-form.test">Test</Trans>
</Button>
)}
@@ -257,6 +283,20 @@ export function ChannelSubForm<R extends ChannelValues>({
</div>
{notifier && (
<div className={styles.innerContent}>
{hasPlaceholderEmail && (
<Alert
title={t(
'alerting.contact-points.email.placeholder-warning-title',
'Configure a valid email address'
)}
severity="info"
>
<Trans i18nKey="alerting.contact-points.email.placeholder-warning-body">
This contact point is using a placeholder email address (<Text variant="code">example@email.com</Text>
). Please update it with a valid email address to receive alerts and enable testing.
</Trans>
</Alert>
)}
{showTelegramWarning && (
<Alert
title={t(
@@ -185,6 +185,7 @@ export const GrafanaReceiverForm = ({ contactPoint, readOnly = false, editMode }
isOpen={!!testReceivers}
alertManagerSourceName={GRAFANA_RULES_SOURCE_NAME}
receivers={testReceivers}
channelValues={testReceivers[0]?.grafana_managed_receiver_configs?.[0]}
/>
)}
</>
@@ -1,14 +1,15 @@
import { css } from '@emotion/css';
import { useState } from 'react';
import { useMemo, useState } from 'react';
import { FormProvider, useForm } from 'react-hook-form';
import { GrafanaTheme2 } from '@grafana/data';
import { Trans, t } from '@grafana/i18n';
import { Alert, Button, Label, Modal, RadioButtonGroup, useStyles2 } from '@grafana/ui';
import { Alert, Button, Label, Modal, RadioButtonGroup, Text, useStyles2 } from '@grafana/ui';
import { Receiver, TestReceiversAlert } from 'app/plugins/datasource/alertmanager/types';
import { Annotations, Labels } from 'app/types/unified-alerting-dto';
import { useTestIntegrationMutation } from '../../../api/receiversApi';
import { GrafanaChannelValues } from '../../../types/receiver-form';
import { defaultAnnotations } from '../../../utils/constants';
import { stringifyErrorLike } from '../../../utils/misc';
import AnnotationsStep from '../../rule-editor/AnnotationsStep';
@@ -19,6 +20,7 @@ interface Props {
onDismiss: () => void;
alertManagerSourceName: string;
receivers: Receiver[];
channelValues?: GrafanaChannelValues;
}
type AnnoField = {
@@ -43,15 +45,34 @@ const defaultValues: FormFields = {
labels: [{ key: '', value: '' }],
};
export const TestContactPointModal = ({ isOpen, onDismiss, alertManagerSourceName, receivers }: Props) => {
export const TestContactPointModal = ({
isOpen,
onDismiss,
alertManagerSourceName,
receivers,
channelValues,
}: Props) => {
const [notificationType, setNotificationType] = useState<NotificationType>(NotificationType.predefined);
const styles = useStyles2(getStyles);
const formMethods = useForm<FormFields>({ defaultValues, mode: 'onBlur' });
const [testIntegration, { isLoading, error, isSuccess }] = useTestIntegrationMutation();
// Check if email integration has placeholder addresses
const hasPlaceholderEmail = useMemo(() => {
if (!channelValues || channelValues.type !== 'email') {
return false;
}
const addresses = channelValues.settings?.addresses;
if (!addresses) {
return false;
}
const placeholders = ['<example@email.com>', 'example@email.com'];
const addressList = typeof addresses === 'string' ? [addresses] : addresses;
return addressList.some((addr: string) => placeholders.includes(addr?.trim()));
}, [channelValues]);
const onSubmit = async (data: FormFields) => {
let alert: TestReceiversAlert | undefined;
if (notificationType === NotificationType.custom) {
alert = {
annotations: data.annotations
@@ -93,6 +114,22 @@ export const TestContactPointModal = ({ isOpen, onDismiss, alertManagerSourceNam
/>
)}
{hasPlaceholderEmail && (
<div className={styles.section}>
<Alert
title={t(
'alerting.test-contact-point-modal.placeholder-warning-title',
'Configure a valid email address'
)}
severity="info"
>
<Trans i18nKey="alerting.test-contact-point-modal.placeholder-warning-body">
This contact point is using a placeholder email address (<Text variant="code">example@email.com</Text>).
Please update it with a valid email address before testing.
</Trans>
</Alert>
</div>
)}
<div className={styles.section}>
<Label>
<Trans i18nKey="alerting.test-contact-point-modal.notification-message">Notification message</Trans>
@@ -132,7 +169,18 @@ export const TestContactPointModal = ({ isOpen, onDismiss, alertManagerSourceNam
)}
<Modal.ButtonRow>
<Button type="submit" disabled={isLoading}>
<Button
type="submit"
disabled={isLoading || hasPlaceholderEmail}
tooltip={
hasPlaceholderEmail
? t(
'alerting.test-contact-point-modal.test-disabled-placeholder',
'Please configure a valid email address before testing'
)
: undefined
}
>
<Trans i18nKey="alerting.test-contact-point-modal.send-test-notification">Send test notification</Trans>
</Button>
</Modal.ButtonRow>