chore: alerts fixes and improvements (#8327)

This commit is contained in:
Amlan Kumar Nandy 2025-06-23 14:08:17 +07:00 committed by GitHub
parent 7f5b388722
commit f0994e52c0
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
8 changed files with 106 additions and 21 deletions

View File

@ -62,5 +62,8 @@
"channel_test_failed": "Failed to send a test message to this channel, please confirm that the parameters are set correctly",
"channel_test_unexpected": "An unexpected error occurred while sending a message to this channel, please try again",
"webhook_url_required": "Webhook URL is mandatory",
"slack_channel_help": "Specify channel or user, use #channel-name, @username (has to be all lowercase, no whitespace)"
"slack_channel_help": "Specify channel or user, use #channel-name, @username (has to be all lowercase, no whitespace)",
"api_key_required": "API Key is mandatory",
"to_required": "To field is mandatory",
"channel_name_required": "Channel name is mandatory"
}

View File

@ -77,5 +77,8 @@
"channel_test_failed": "Failed to send a test message to this channel, please confirm that the parameters are set correctly",
"channel_test_unexpected": "An unexpected error occurred while sending a message to this channel, please try again",
"webhook_url_required": "Webhook URL is mandatory",
"slack_channel_help": "Specify channel or user, use #channel-name, @username (has to be all lowercase, no whitespace)"
"slack_channel_help": "Specify channel or user, use #channel-name, @username (has to be all lowercase, no whitespace)",
"api_key_required": "API Key is mandatory",
"to_required": "To field is mandatory",
"channel_name_required": "Channel name is mandatory"
}

View File

@ -120,14 +120,19 @@ describe('Create Alert Channel', () => {
expect(screen.getByText('button_test_channel')).toBeInTheDocument();
expect(screen.getByText('button_return')).toBeInTheDocument();
});
it('Should check if saving the form without filling the name displays "Something went wrong"', async () => {
it('Should check if saving the form without filling the name displays error notification', async () => {
const saveButton = screen.getByRole('button', {
name: 'button_save_channel',
});
fireEvent.click(saveButton);
await waitFor(() => expect(showErrorModal).toHaveBeenCalled());
await waitFor(() =>
expect(errorNotification).toHaveBeenCalledWith({
message: 'Error',
description: 'channel_name_required',
}),
);
});
it('Should check if clicking on Test button shows "An alert has been sent to this channel" success message if testing passes', async () => {
server.use(

View File

@ -138,6 +138,14 @@ function CreateAlertChannels({
);
const onSlackHandler = useCallback(async () => {
if (!selectedConfig.api_url) {
notifications.error({
message: 'Error',
description: t('webhook_url_required'),
});
return;
}
setSavingState(true);
try {
@ -154,7 +162,7 @@ function CreateAlertChannels({
} finally {
setSavingState(false);
}
}, [prepareSlackRequest, notifications, t, showErrorModal]);
}, [selectedConfig, notifications, t, prepareSlackRequest, showErrorModal]);
const prepareWebhookRequest = useCallback(() => {
// initial api request without auth params
@ -192,6 +200,14 @@ function CreateAlertChannels({
}, [notifications, t, selectedConfig]);
const onWebhookHandler = useCallback(async () => {
if (!selectedConfig.api_url) {
notifications.error({
message: 'Error',
description: t('webhook_url_required'),
});
return;
}
setSavingState(true);
try {
const request = prepareWebhookRequest();
@ -208,7 +224,13 @@ function CreateAlertChannels({
} finally {
setSavingState(false);
}
}, [prepareWebhookRequest, notifications, t, showErrorModal]);
}, [
selectedConfig.api_url,
notifications,
t,
prepareWebhookRequest,
showErrorModal,
]);
const preparePagerRequest = useCallback(() => {
const validationError = ValidatePagerChannel(selectedConfig as PagerChannel);
@ -272,6 +294,14 @@ function CreateAlertChannels({
);
const onOpsgenieHandler = useCallback(async () => {
if (!selectedConfig.api_key) {
notifications.error({
message: 'Error',
description: t('api_key_required'),
});
return;
}
setSavingState(true);
try {
await createOpsgenie(prepareOpsgenieRequest());
@ -287,7 +317,13 @@ function CreateAlertChannels({
} finally {
setSavingState(false);
}
}, [prepareOpsgenieRequest, notifications, t, showErrorModal]);
}, [
selectedConfig.api_key,
notifications,
t,
prepareOpsgenieRequest,
showErrorModal,
]);
const prepareEmailRequest = useCallback(
() => ({
@ -301,6 +337,14 @@ function CreateAlertChannels({
);
const onEmailHandler = useCallback(async () => {
if (!selectedConfig.to) {
notifications.error({
message: 'Error',
description: t('to_required'),
});
return;
}
setSavingState(true);
try {
const request = prepareEmailRequest();
@ -317,7 +361,7 @@ function CreateAlertChannels({
} finally {
setSavingState(false);
}
}, [prepareEmailRequest, notifications, t, showErrorModal]);
}, [prepareEmailRequest, notifications, t, showErrorModal, selectedConfig.to]);
const prepareMsTeamsRequest = useCallback(
() => ({
@ -331,6 +375,14 @@ function CreateAlertChannels({
);
const onMsTeamsHandler = useCallback(async () => {
if (!selectedConfig.webhook_url) {
notifications.error({
message: 'Error',
description: t('webhook_url_required'),
});
return;
}
setSavingState(true);
try {
@ -347,10 +399,24 @@ function CreateAlertChannels({
} finally {
setSavingState(false);
}
}, [prepareMsTeamsRequest, notifications, t, showErrorModal]);
}, [
selectedConfig.webhook_url,
notifications,
t,
prepareMsTeamsRequest,
showErrorModal,
]);
const onSaveHandler = useCallback(
async (value: ChannelType) => {
if (!selectedConfig.name) {
notifications.error({
message: 'Error',
description: t('channel_name_required'),
});
return;
}
const functionMapper = {
[ChannelType.Slack]: onSlackHandler,
[ChannelType.Webhook]: onWebhookHandler,

View File

@ -28,26 +28,26 @@ function ExapandableRow({ allAlerts }: ExapandableRowProps): JSX.Element {
hoverable
key={alert.fingerprint}
>
<TableCell>
<TableCell minWidth="90px">
<Status severity={alert.status.state} />
</TableCell>
<TableCell>
<TableCell minWidth="90px" overflowX="scroll">
<Typography>{labels.alertname}</Typography>
</TableCell>
<TableCell>
<TableCell minWidth="90px">
<Typography>{labels.severity}</Typography>
</TableCell>
<TableCell>
<TableCell minWidth="90px">
<Typography>{`${formatTimezoneAdjustedTimestamp(
formatedDate,
DATE_TIME_FORMATS.UTC_US,
)}`}</Typography>
</TableCell>
<TableCell>
<TableCell minWidth="90px" overflowX="scroll">
<div>
{tags.map((e) => (
<Tag key={e}>{`${e}:${labels[e]}`}</Tag>

View File

@ -19,7 +19,7 @@ function TableRowComponent({
return (
<div>
<TableRow>
<TableCell>
<TableCell minWidth="90px">
<StatusContainer>
<IconContainer onClick={onClickHandler}>
{!isClicked ? <PlusSquareOutlined /> : <MinusSquareOutlined />}
@ -33,10 +33,10 @@ function TableRowComponent({
</>
</StatusContainer>
</TableCell>
<TableCell />
<TableCell />
<TableCell />
<TableCell />
<TableCell minWidth="90px" />
<TableCell minWidth="90px" />
<TableCell minWidth="90px" />
<TableCell minWidth="90px" />
{/* <TableCell minWidth="200px">
<Button type="primary">Resume Group</Button>
</TableCell> */}

View File

@ -36,7 +36,9 @@ function FilteredTable({
<Container>
<TableHeaderContainer>
{headers.map((header) => (
<TableHeader key={header}>{header}</TableHeader>
<TableHeader key={header} minWidth="90px">
{header}
</TableHeader>
))}
</TableHeaderContainer>

View File

@ -1,13 +1,14 @@
import { Card } from 'antd';
import styled from 'styled-components';
export const TableHeader = styled(Card)`
export const TableHeader = styled(Card)<Props>`
&&& {
flex: 1;
text-align: center;
.ant-card-body {
padding: 1rem;
}
min-width: ${(props): string => props.minWidth || ''};
}
`;
@ -37,6 +38,7 @@ export const TableRow = styled(Card)`
interface Props {
minWidth?: string;
overflowX?: string;
}
export const TableCell = styled.div<Props>`
&&& {
@ -45,6 +47,10 @@ export const TableCell = styled.div<Props>`
display: flex;
justify-content: flex-start;
align-items: center;
overflow-x: ${(props): string => props.overflowX || 'none'};
::-webkit-scrollbar {
height: ${(props): string => (props.overflowX ? '2px' : '8px')};
}
}
`;