mirror of
https://github.com/bluewave-labs/Checkmate.git
synced 2026-01-08 18:59:43 -06:00
Merge pull request #2420 from bluewave-labs/feat/test-notification
feat: test notification
This commit is contained in:
@@ -152,10 +152,36 @@ const useEditNotification = () => {
|
||||
return [editNotification, isLoading, error];
|
||||
};
|
||||
|
||||
const useTestNotification = () => {
|
||||
const [isLoading, setIsLoading] = useState(false);
|
||||
const [error, setError] = useState(null);
|
||||
const { t } = useTranslation();
|
||||
|
||||
const testNotification = async (notification) => {
|
||||
try {
|
||||
setIsLoading(true);
|
||||
await networkService.testNotification({ notification });
|
||||
createToast({
|
||||
body: t("notifications.test.success"),
|
||||
});
|
||||
} catch (error) {
|
||||
setError(error);
|
||||
createToast({
|
||||
body: t("notifications.test.failed"),
|
||||
});
|
||||
} finally {
|
||||
setIsLoading(false);
|
||||
}
|
||||
};
|
||||
|
||||
return [testNotification, isLoading, error];
|
||||
};
|
||||
|
||||
export {
|
||||
useCreateNotification,
|
||||
useGetNotificationsByTeamId,
|
||||
useDeleteNotification,
|
||||
useGetNotificationById,
|
||||
useEditNotification,
|
||||
useTestNotification,
|
||||
};
|
||||
|
||||
@@ -16,6 +16,7 @@ import {
|
||||
useCreateNotification,
|
||||
useGetNotificationById,
|
||||
useEditNotification,
|
||||
useTestNotification,
|
||||
} from "../../../Hooks/useNotifications";
|
||||
import {
|
||||
notificationEmailValidation,
|
||||
@@ -35,6 +36,8 @@ const CreateNotifications = () => {
|
||||
const [createNotification, isCreating, createNotificationError] =
|
||||
useCreateNotification();
|
||||
const [editNotification, isEditing, editNotificationError] = useEditNotification();
|
||||
const [testNotification, isTesting, testNotificationError] = useTestNotification();
|
||||
|
||||
const BREADCRUMBS = [
|
||||
{ name: "notifications", path: "/notifications" },
|
||||
{ name: "create", path: "/notifications/create" },
|
||||
@@ -178,6 +181,56 @@ const CreateNotifications = () => {
|
||||
setNotification(newNotification);
|
||||
};
|
||||
|
||||
const onTestNotification = () => {
|
||||
const form = {
|
||||
...notification,
|
||||
type: NOTIFICATION_TYPES.find((type) => type._id === notification.type).value,
|
||||
};
|
||||
|
||||
if (notification.type === 2) {
|
||||
form.type = "webhook";
|
||||
}
|
||||
|
||||
let error = null;
|
||||
|
||||
if (form.type === "email") {
|
||||
error = notificationEmailValidation.validate(
|
||||
{ notificationName: form.notificationName, address: form.address },
|
||||
{ abortEarly: false }
|
||||
).error;
|
||||
} else if (form.type === "webhook") {
|
||||
form.config = {
|
||||
platform: form.config.platform,
|
||||
webhookUrl: form.config.webhookUrl,
|
||||
};
|
||||
error = notificationWebhookValidation.validate(
|
||||
{ notificationName: form.notificationName, config: form.config },
|
||||
{ abortEarly: false }
|
||||
).error;
|
||||
} else if (form.type === "pager_duty") {
|
||||
form.config = {
|
||||
platform: form.config.platform,
|
||||
routingKey: form.config.routingKey,
|
||||
};
|
||||
error = notificationPagerDutyValidation.validate(
|
||||
{ notificationName: form.notificationName, config: form.config },
|
||||
{ abortEarly: false }
|
||||
).error;
|
||||
}
|
||||
|
||||
if (error) {
|
||||
const newErrors = {};
|
||||
error.details.forEach((err) => {
|
||||
newErrors[err.path[0]] = err.message;
|
||||
});
|
||||
createToast({ body: "Please check the form for errors." });
|
||||
setErrors(newErrors);
|
||||
return;
|
||||
}
|
||||
|
||||
testNotification(form);
|
||||
};
|
||||
|
||||
return (
|
||||
<Stack gap={theme.spacing(10)}>
|
||||
<Breadcrumbs list={BREADCRUMBS} />
|
||||
@@ -357,7 +410,16 @@ const CreateNotifications = () => {
|
||||
<Stack
|
||||
direction="row"
|
||||
justifyContent="flex-end"
|
||||
spacing={theme.spacing(2)}
|
||||
>
|
||||
<Button
|
||||
loading={isCreating || isEditing || notificationIsLoading}
|
||||
variant="contained"
|
||||
color="secondary"
|
||||
onClick={onTestNotification}
|
||||
>
|
||||
Test notification
|
||||
</Button>
|
||||
<Button
|
||||
loading={isCreating || isEditing || notificationIsLoading}
|
||||
type="submit"
|
||||
|
||||
@@ -56,7 +56,16 @@ const Notifications = () => {
|
||||
id: "target",
|
||||
content: "Target",
|
||||
render: (row) => {
|
||||
return row.address || row.config?.webhookUrl || row.config?.routingKey;
|
||||
const type = row.type;
|
||||
if (type === "email") {
|
||||
return row.address;
|
||||
}
|
||||
if (type === "webhook") {
|
||||
return row.config?.webhookUrl;
|
||||
}
|
||||
if (type === "pager_duty") {
|
||||
return row.config?.routingKey;
|
||||
}
|
||||
},
|
||||
},
|
||||
{
|
||||
|
||||
@@ -703,18 +703,11 @@ class NetworkService {
|
||||
* @returns {Promise<AxiosResponse>} The response from the axios POST request.
|
||||
*/
|
||||
async testNotification(config) {
|
||||
return this.axiosInstance.post(
|
||||
"/notifications/test-webhook",
|
||||
{
|
||||
platform: config.platform,
|
||||
...config.payload,
|
||||
return this.axiosInstance.post("/notifications/test", config.notification, {
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
{
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
}
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -363,6 +363,10 @@
|
||||
"edit": {
|
||||
"success": "Notification updated successfully",
|
||||
"failed": "Failed to update notification"
|
||||
},
|
||||
"test": {
|
||||
"success": "Test notification sent successfully",
|
||||
"failed": "Failed to send test notification"
|
||||
}
|
||||
},
|
||||
"testLocale": "testLocale",
|
||||
|
||||
@@ -24,7 +24,6 @@ class NotificationController {
|
||||
this.statusService = statusService;
|
||||
this.db = db;
|
||||
this.triggerNotification = this.triggerNotification.bind(this);
|
||||
this.testWebhook = this.testWebhook.bind(this);
|
||||
}
|
||||
|
||||
async triggerNotification(req, res, next) {
|
||||
@@ -85,94 +84,38 @@ class NotificationController {
|
||||
};
|
||||
}
|
||||
|
||||
handleTelegramTest(botToken, chatId) {
|
||||
if (!botToken || !chatId) {
|
||||
return {
|
||||
isValid: false,
|
||||
error: {
|
||||
msg: this.stringService.telegramRequiresBotTokenAndChatId,
|
||||
status: 400,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
return {
|
||||
isValid: true,
|
||||
notification: {
|
||||
type: NOTIFICATION_TYPES.WEBHOOK,
|
||||
platform: PLATFORMS.TELEGRAM,
|
||||
config: { botToken, chatId },
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
handleWebhookTest(webhookUrl, platform) {
|
||||
if (webhookUrl === null) {
|
||||
return {
|
||||
isValid: false,
|
||||
error: {
|
||||
msg: this.stringService.webhookUrlRequired,
|
||||
status: 400,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
return {
|
||||
isValid: true,
|
||||
notification: {
|
||||
type: NOTIFICATION_TYPES.WEBHOOK,
|
||||
platform: platform,
|
||||
config: { webhookUrl },
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
async testWebhook(req, res, next) {
|
||||
testNotification = async (req, res, next) => {
|
||||
try {
|
||||
const { webhookUrl, platform, botToken, chatId } = req.body;
|
||||
const notification = req.body;
|
||||
let success;
|
||||
|
||||
if (platform === null) {
|
||||
if (notification?.type === "email") {
|
||||
success = await this.notificationService.sendTestEmail(notification);
|
||||
}
|
||||
|
||||
if (notification?.type === "webhook") {
|
||||
success =
|
||||
await this.notificationService.sendTestWebhookNotification(notification);
|
||||
}
|
||||
|
||||
if (notification?.type === "pager_duty") {
|
||||
success =
|
||||
await this.notificationService.sendTestPagerDutyNotification(notification);
|
||||
}
|
||||
if (!success) {
|
||||
return res.error({
|
||||
msg: this.stringService.platformRequired,
|
||||
msg: "Sending notification failed",
|
||||
status: 400,
|
||||
});
|
||||
}
|
||||
|
||||
// Platform-specific handling
|
||||
const platformHandlers = {
|
||||
[PLATFORMS.TELEGRAM]: () => this.handleTelegramTest(botToken, chatId),
|
||||
// Default handler for webhook-based platforms (Slack, Discord, etc.)
|
||||
default: () => this.handleWebhookTest(webhookUrl, platform),
|
||||
};
|
||||
|
||||
const handler = platformHandlers[platform] || platformHandlers.default;
|
||||
const handlerResult = handler();
|
||||
|
||||
if (!handlerResult.isValid) {
|
||||
return res.error(handlerResult.error);
|
||||
}
|
||||
|
||||
const networkResponse = this.createTestNetworkResponse();
|
||||
|
||||
const result = await this.notificationService.sendWebhookNotification(
|
||||
networkResponse,
|
||||
handlerResult.notification
|
||||
);
|
||||
|
||||
if (result && result !== false) {
|
||||
return res.success({
|
||||
msg: this.stringService.webhookSendSuccess,
|
||||
});
|
||||
} else {
|
||||
return res.error({
|
||||
msg: this.stringService.testNotificationFailed,
|
||||
status: 400,
|
||||
});
|
||||
}
|
||||
return res.success({
|
||||
msg: "Notification sent successfully",
|
||||
});
|
||||
} catch (error) {
|
||||
next(handleError(error, SERVICE_NAME, "testWebhook"));
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
createNotification = async (req, res, next) => {
|
||||
try {
|
||||
|
||||
@@ -13,7 +13,7 @@ class NotificationRoutes {
|
||||
|
||||
this.router.post("/trigger", this.notificationController.triggerNotification);
|
||||
|
||||
this.router.post("/test-webhook", this.notificationController.testWebhook);
|
||||
this.router.post("/test", this.notificationController.testNotification);
|
||||
|
||||
this.router.post("/", this.notificationController.createNotification);
|
||||
|
||||
|
||||
@@ -463,6 +463,29 @@ class NetworkService {
|
||||
}
|
||||
}
|
||||
|
||||
async requestPagerDuty({ message, routingKey, monitorUrl }) {
|
||||
try {
|
||||
const response = await this.axios.post(`https://events.pagerduty.com/v2/enqueue`, {
|
||||
routing_key: routingKey,
|
||||
event_action: "trigger",
|
||||
payload: {
|
||||
summary: message,
|
||||
severity: "critical",
|
||||
source: monitorUrl,
|
||||
timestamp: new Date().toISOString(),
|
||||
},
|
||||
});
|
||||
|
||||
if (response?.data?.status !== "success") return false;
|
||||
return true;
|
||||
} catch (error) {
|
||||
error.details = error.response?.data;
|
||||
error.service = this.SERVICE_NAME;
|
||||
error.method = "requestPagerDuty";
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the status of a job based on its type and returns the appropriate response.
|
||||
*
|
||||
|
||||
@@ -95,6 +95,17 @@ class NotificationService {
|
||||
return MESSAGE_FORMATTERS[platform](messageText, chatId);
|
||||
}
|
||||
|
||||
sendTestWebhookNotification = async (notification) => {
|
||||
const config = notification.config;
|
||||
const response = await this.networkService.requestWebhook(
|
||||
config.platform,
|
||||
config.webhookUrl,
|
||||
"This is a test notification"
|
||||
);
|
||||
|
||||
return response.status;
|
||||
};
|
||||
|
||||
/**
|
||||
* Sends a webhook notification to a specified platform.
|
||||
*
|
||||
@@ -185,6 +196,28 @@ class NotificationService {
|
||||
return true;
|
||||
}
|
||||
|
||||
sendTestEmail = async (notification) => {
|
||||
const to = notification?.address;
|
||||
if (!to || typeof to !== "string") {
|
||||
throw new Error(this.stringService.errorForValidEmailAddress);
|
||||
}
|
||||
|
||||
const subject = this.stringService.testEmailSubject;
|
||||
const context = { testName: "Monitoring System" };
|
||||
|
||||
const messageId = await this.emailService.buildAndSendEmail(
|
||||
"testEmailTemplate",
|
||||
context,
|
||||
to,
|
||||
subject
|
||||
);
|
||||
|
||||
if (messageId) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
};
|
||||
|
||||
/**
|
||||
* Sends an email notification about monitor status change
|
||||
*
|
||||
@@ -194,6 +227,7 @@ class NotificationService {
|
||||
* @param {string} address - Email address to send the notification to
|
||||
* @returns {Promise<boolean>} - Indicates email was sent successfully
|
||||
*/
|
||||
|
||||
async sendEmail(networkResponse, address) {
|
||||
const { monitor, status, prevStatus } = networkResponse;
|
||||
const template = prevStatus === false ? "serverIsUpTemplate" : "serverIsDownTemplate";
|
||||
@@ -203,6 +237,16 @@ class NotificationService {
|
||||
return true;
|
||||
}
|
||||
|
||||
async sendTestPagerDutyNotification(notification) {
|
||||
const { routingKey } = notification.config;
|
||||
const response = await this.networkService.requestPagerDuty({
|
||||
message: "This is a test notification",
|
||||
monitorUrl: "Test notification",
|
||||
routingKey,
|
||||
});
|
||||
|
||||
return response;
|
||||
}
|
||||
async sendPagerDutyNotification(networkResponse, notification) {
|
||||
const { monitor, status, code } = networkResponse;
|
||||
const { routingKey, platform } = notification.config;
|
||||
@@ -222,7 +266,7 @@ class NotificationService {
|
||||
routingKey,
|
||||
monitorUrl: monitor.url,
|
||||
});
|
||||
return response.status;
|
||||
return response;
|
||||
} catch (error) {
|
||||
this.logger.error({
|
||||
message: "Failed to send PagerDuty notification",
|
||||
|
||||
Reference in New Issue
Block a user