Add route, controller, db operations, and validation for aggregate monitor stats

This commit is contained in:
Alex Holliday
2024-09-03 14:39:16 -07:00
parent 88316de306
commit bc0df325d1
6 changed files with 137 additions and 2 deletions
+41 -1
View File
@@ -6,12 +6,13 @@ const {
editMonitorBodyValidation,
getMonitorsByTeamIdQueryValidation,
pauseMonitorParamValidation,
getMonitorAggregateStatsParamValidation,
getMonitorAggregateStatsQueryValidation,
} = require("../validation/joi");
const sslChecker = require("ssl-checker");
const SERVICE_NAME = "monitorController";
const { errorMessages, successMessages } = require("../utils/messages");
const { runInNewContext } = require("vm");
/**
* Returns all monitors
@@ -35,6 +36,44 @@ const getAllMonitors = async (req, res, next) => {
}
};
/**
* Returns agregate stats for a monitor
* @async
* @param {Express.Request} req
* @param {Express.Response} res
* @returns {Promise<Express.Response>}
* @throws {Error}
*/
const getMonitorAggregateStats = async (req, res, next) => {
try {
await getMonitorAggregateStatsParamValidation.validateAsync(req.params);
await getMonitorAggregateStatsQueryValidation.validateAsync(req.query);
} catch (error) {
error.status = 422;
error.message =
error.details?.[0]?.message || error.message || "Validation Error";
next(error);
return;
}
try {
const { monitorId } = req.params;
const dateRange = req.query.dateRange;
const aggregateStats = await req.db.getMonitorAggregateStats(
monitorId,
dateRange
);
return res.json({
success: true,
msg: successMessages.MONTIOR_STATS_BY_ID,
data: aggregateStats,
});
} catch (error) {
next(error);
}
};
/**
* Returns monitor stats for monitor with matching ID
* @async
@@ -376,6 +415,7 @@ const pauseMonitor = async (req, res, next) => {
module.exports = {
getAllMonitors,
getMonitorAggregateStats,
getMonitorStatsById,
getMonitorCertificate,
getMonitorById,
+2
View File
@@ -64,6 +64,7 @@ const {
const {
getAllMonitors,
getMonitorAggregateStats,
getMonitorStatsById,
getMonitorById,
getMonitorsByTeamId,
@@ -144,6 +145,7 @@ module.exports = {
resetPassword,
checkSuperadmin,
getAllMonitors,
getMonitorAggregateStats,
getMonitorStatsById,
getMonitorById,
getMonitorsByTeamId,
+83
View File
@@ -143,6 +143,88 @@ const getStatusBarValues = (monitor, checks) => {
}
return statusBarValues.reverse();
};
/**
* Get aggregate monitor stats for charts
* @async
* @param {Express.Request} req
* @param {Express.Response} res
* @returns {Promise<Monitor>}
* @throws {Error}
*/
const getMonitorAggregateStats = async (monitorId, dateRange) => {
const startDates = {
day: new Date(new Date().setDate(new Date().getDate() - 1)),
week: new Date(new Date().setDate(new Date().getDate() - 7)),
month: new Date(new Date().setMonth(new Date().getMonth() - 1)),
};
try {
const startDate = startDates[dateRange];
if (!startDate) {
throw new Error("Invalid date range specified");
}
const endDate = new Date();
const checks = await Check.find({
monitorId: monitorId,
createdAt: {
$gte: startDate,
$lte: endDate,
},
});
let groupedChecks;
// Group checks by hour if range is day
if (dateRange === "day") {
groupedChecks = checks.reduce((acc, check) => {
const hour = new Date(check.createdAt).getHours();
if (!acc[hour]) {
acc[hour] = { hour, checks: [] };
}
acc[hour].checks.push(check);
return acc;
}, {});
}
// Group checks by day if range is week or month
else {
groupedChecks = checks.reduce((acc, check) => {
const day = new Date(check.createdAt).toISOString().split("T")[0]; // Extract the date part
if (!acc[day]) {
acc[day] = { day, checks: [] };
}
acc[day].checks.push(check);
return acc;
}, {});
}
// Map grouped checks to stats
stats = Object.values(groupedChecks).map((group) => {
const totalChecks = group.checks.length;
const totalIncidents = group.checks.filter(
(check) => check.status === false
).length;
const avgResponseTime =
group.checks.reduce((sum, check) => sum + check.responseTime, 0) /
totalChecks;
return {
hour: group.hour,
day: group.day,
totalChecks,
totalIncidents,
avgResponseTime: `${avgResponseTime.toFixed(2)}ms`,
};
});
return stats;
} catch (error) {
throw error;
}
};
/**
* Get stats by monitor ID
* @async
@@ -452,6 +534,7 @@ const editMonitor = async (candidateId, candidateMonitor) => {
module.exports = {
getAllMonitors,
getMonitorAggregateStats,
getMonitorStatsById,
getMonitorById,
getMonitorsByTeamId,
+1
View File
@@ -3,6 +3,7 @@ const monitorController = require("../controllers/monitorController");
const { isAllowed } = require("../middleware/isAllowed");
router.get("/", monitorController.getAllMonitors);
router.get("/aggregate/:monitorId", monitorController.getMonitorAggregateStats);
router.get("/stats/:monitorId", monitorController.getMonitorStatsById);
router.get("/certificate/:monitorId", monitorController.getMonitorCertificate);
router.get("/:monitorId", monitorController.getMonitorById);
+9
View File
@@ -201,6 +201,13 @@ const pauseMonitorParamValidation = joi.object({
monitorId: joi.string().required(),
});
const getMonitorAggregateStatsParamValidation = joi.object({
monitorId: joi.string().required(),
});
const getMonitorAggregateStatsQueryValidation = joi.object({
dateRange: joi.string().valid("day", "week", "month"),
});
//****************************************
// Alerts
//****************************************
@@ -357,6 +364,8 @@ module.exports = {
getMonitorByIdQueryValidation,
getMonitorsByTeamIdValidation,
getMonitorsByTeamIdQueryValidation,
getMonitorAggregateStatsParamValidation,
getMonitorAggregateStatsQueryValidation,
editMonitorBodyValidation,
pauseMonitorParamValidation,
editUserParamValidation,