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:
@@ -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]}
|
||||
/>
|
||||
)}
|
||||
</>
|
||||
|
||||
+53
-5
@@ -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>
|
||||
|
||||
Reference in New Issue
Block a user