Feat/monitor endpoint refactor (#463)

* Moved all methods to modules

* removed old mongoDB file

* Refactored endpoints to take query params, removed duplicate methods, updated routes, updated Client queries

* Added comment

* Add model differentiation

* Add sort order, fix minor bug, add validation for query params
This commit is contained in:
Alexander Holliday authored and GitHub committed 2024-07-26 18:56:39 -07:00
1 parent e47d3685ce
commit 477cad7cc9
11 files changed
+132 -166

No files matched your search

@@ -51,7 +51,7 @@ export const getMonitorsByUserId = createAsyncThunk(
const user = jwtDecode(token);
try {
const res = await axiosInstance.get(
`/monitors/user/${user._id}?limit=25`,
`/monitors/user/${user._id}?limit=25&type=http&type=ping&sortOrder=desc`,
{
headers: {
Authorization: `Bearer ${token}`,
+1 -1
View File
@@ -33,7 +33,7 @@ const Incidents = () => {
useEffect(() => {
const fetchIncidents = async () => {
const res = await axiosInstance.get(
`/monitors/incidents/user/${authState.user._id}`,
`/monitors/user/${authState.user._id}?status=false`,
{
headers: {
Authorization: `Bearer ${authState.authToken}`,
+8 -5
View File
@@ -82,11 +82,14 @@ const DetailsPage = () => {
useEffect(() => {
const fetchMonitor = async () => {
const res = await axiosInstance.get(`/monitors/${monitorId}`, {
headers: {
Authorization: `Bearer ${authToken}`,
},
});
const res = await axiosInstance.get(
`/monitors/${monitorId}?sortOrder=asc`,
{
headers: {
Authorization: `Bearer ${authToken}`,
},
}
);
setMonitor(res.data.data);
const data = {
cols: [
+4 -1
View File
@@ -100,6 +100,9 @@ const Monitors = () => {
: "var(--env-var-color-19)",
};
// Reverse checks so latest check is on the right
const reversedChecks = monitor.checks.slice().reverse();
return {
id: monitor._id,
handleClick: () => navigate(`/monitors/${monitor._id}`),
@@ -117,7 +120,7 @@ const Monitors = () => {
/>
),
},
{ id: idx + 2, data: <ResponseTimeChart checks={monitor.checks} /> },
{ id: idx + 2, data: <ResponseTimeChart checks={reversedChecks} /> },
{ id: idx + 3, data: monitor.type },
{ id: idx + 4, data: "TODO" },
],
+19 -6
View File
@@ -6,16 +6,12 @@
![](https://img.shields.io/github/issues-pr/bluewave-labs/bluewave-uptime)
![](https://img.shields.io/github/issues/bluewave-labs/bluewave-uptime)
<h1 align="center"><a href="https://bluewavelabs.ca" target="_blank">BlueWave Uptime</a></h1>
<p align="center"><strong>An open source server monitoring application</strong></p>
![bluewave-uptime-screenshot](https://github.com/user-attachments/assets/765bf060-e734-45e6-bd00-2a2eb37c9c02)
BlueWave Uptime is an open source server monitoring application used to track the operational status and performance of servers and websites. It regularly checks whether a server/website is accessible and performs optimally, providing real-time alerts and reports on the monitored services' availability, downtime, and response time.
## Contributing
@@ -776,7 +772,7 @@ curl --request GET \
</details>
<details>
<summary id='get-monitor-id'><code>GET</code> <b>/api/v1/monitor/{id}</b></summary>
<summary id='get-monitor-id'><code>GET</code> <b>/api/v1/monitor/{id}?status</b></summary>
###### Method/Headers
@@ -785,6 +781,14 @@ curl --request GET \
> | Method | GET |
> | content-type | application/json |
###### Query Params
> | Name | Type | Required | Notes |
> | --------- | --------- | -------- | ------------------------------------------------ |
> | status | `boolean` | Optional | Check status |
> | limit | `number` | Optional | Number of checks to return |
> | sortOrder | `string` | Optional | `desc`:Newest -> Oldest, `asc`: Oldest -> Newest |
###### Response Payload
> | Type | Notes |
@@ -795,7 +799,7 @@ curl --request GET \
```
curl --request GET \
--url http://localhost:5000/api/v1/monitors/664d070786e62625ac612ca1 \
--url http://localhost:5000/api/v1/monitors/664d070786e62625ac612ca1?status=true?limit=0 \
--header '<bearer_token>' \
```
@@ -857,6 +861,15 @@ curl --request GET \
> | Method | GET |
> | content-type | application/json |
###### Query Params
> | Name | Type | Required | Notes |
> | --------- | --------- | -------- | ------------------------------------------------- |
> | status | `boolean` | Optional | Check status |
> | type | `string` | Optional | Multiple allowed: `http` \| `ping` \| `pagespeed` |
> | limit | `number` | Optional | Monitor status |
> | sortOrder | `string` | Optional | `desc`:Newest -> Oldest, `asc`: Oldest -> Newest |
###### Response Payload
> | Type | Notes |
+7 -65
View File
@@ -1,8 +1,10 @@
const {
getMonitorByIdValidation,
getMonitorByIdParamValidation,
getMonitorByIdQueryValidation,
getMonitorsByUserIdValidation,
monitorValidation,
editMonitorBodyValidation,
getMonitorsByUserIdQueryValidation,
} = require("../validation/joi");
const SERVICE_NAME = "monitorController";
@@ -41,7 +43,8 @@ const getAllMonitors = async (req, res, next) => {
*/
const getMonitorById = async (req, res, next) => {
try {
await getMonitorByIdValidation.validateAsync(req.params);
await getMonitorByIdParamValidation.validateAsync(req.params);
await getMonitorByIdQueryValidation.validateAsync(req.query);
} catch (error) {
error.status = 422;
error.message = error.details[0].message;
@@ -77,14 +80,9 @@ const getMonitorById = async (req, res, next) => {
* @throws {Error}
*/
const getMonitorsByUserId = async (req, res, next) => {
const { error } = getMonitorsByUserIdValidation.validate(req.params);
if (error) {
return res
.status(422)
.json({ success: false, msg: error.details[0].message });
}
try {
await getMonitorsByUserIdValidation.validateAsync(req.params);
await getMonitorsByUserIdQueryValidation.validateAsync(req.query);
const userId = req.params.userId;
const monitors = await req.db.getMonitorsByUserId(req, res);
@@ -99,60 +97,6 @@ const getMonitorsByUserId = async (req, res, next) => {
}
};
/**
* Returns monitor with matching ID and incidents
* @async
* @param {Express.Request} req
* @param {Express.Response} res
* @returns {Promise<Express.Response>}
* @throws {Error}
*/
const getMonitorByIdForIncidents = async (req, res, next) => {
try {
await getMonitorByIdValidation.validateAsync(req.params);
let monitorWithIncidents = await req.db.getMonitorByIdForIncidents(
req,
res
);
return res.json({
success: true,
msg: successMessages.MONTIOR_GET_BY_ID,
data: monitorWithIncidents,
});
} catch (error) {
error.service = SERVICE_NAME;
next(error);
}
};
/**
* Returns all monitors with incidents that belong to User with UserID
* @async
* @param {Express.Request} req
* @param {Express.Response} res
* @returns {Promise<Express.Response>}
* @throws {Error}
*/
const getMonitorsByUserIdForIncidents = async (req, res, next) => {
try {
await getMonitorsByUserIdValidation.validateAsync(req.params);
let monitorsWithIncidents = await req.db.getMonitorsByUserIdForIncidents(
req,
res
);
return res.json({
success: true,
msg: successMessages.MONITOR_GET_BY_USER_ID(req.params.userId),
data: monitorsWithIncidents,
});
} catch (error) {
error.service = SERVICE_NAME;
next(error);
}
};
/**
* Creates a new monitor
* @async
@@ -282,8 +226,6 @@ module.exports = {
getAllMonitors,
getMonitorById,
getMonitorsByUserId,
getMonitorByIdForIncidents,
getMonitorsByUserIdForIncidents,
createMonitor,
deleteMonitor,
deleteAllMonitors,
+68 -74
View File
@@ -1,6 +1,6 @@
const Monitor = require("../../../models/Monitor");
const Check = require("../../../models/Check");
const PageSpeedCheck = require("../../../models/PageSpeedCheck");
const { errorMessages } = require("../../../utils/messages");
/**
@@ -30,10 +30,40 @@ const getAllMonitors = async (req, res) => {
*/
const getMonitorById = async (req, res) => {
try {
const monitor = await Monitor.findById(req.params.monitorId);
const checks = await Check.find({ monitorId: monitor._id }).sort({
createdAt: 1,
});
const { monitorId } = req.params;
let { status, limit, sortOrder } = req.query;
// This effectively removes limit, returning all checks
if (limit === undefined) limit = 0;
// Default sort order is newest -> oldest
if (sortOrder === "asc") {
sortOrder = 1;
} else if (sortOrder === "desc") {
sortOrder = -1;
} else sortOrder = -1;
const monitor = await Monitor.findById(monitorId);
const checksQuery = { monitorId: monitor._id };
if (status !== undefined) {
checksQuery.status = status;
}
// Determine model type
let model =
monitor.type === "http" || monitor.type === "ping"
? Check
: PageSpeedCheck;
const checks = await model
.find(checksQuery)
.sort({
createdAt: sortOrder,
})
.limit(limit);
const monitorWithChecks = { ...monitor.toObject(), checks };
return monitorWithChecks;
} catch (error) {
@@ -51,79 +81,45 @@ const getMonitorById = async (req, res) => {
*/
const getMonitorsByUserId = async (req, res) => {
try {
const limit = req.query ? req.query.limit : undefined;
const monitors = await Monitor.find({ userId: req.params.userId });
console.log("monitors", monitors);
let { limit, type, status, sortOrder } = req.query;
const monitorQuery = { userId: req.params.userId };
if (type !== undefined) {
const types = Array.isArray(type) ? type : [type];
monitorQuery.type = { $in: types };
}
// Default sort order is newest -> oldest
if (sortOrder === "asc") {
sortOrder = 1;
} else if (sortOrder === "desc") {
sortOrder = -1;
} else sortOrder = -1;
// This effectively removes limit, returning all checks
if (limit === undefined) limit = 0;
const monitors = await Monitor.find(monitorQuery);
// Map each monitor to include its associated checks
const monitorsWithChecks = await Promise.all(
monitors.map(async (monitor) => {
if (limit) {
// Checks are order newest -> oldest
const checks = await Check.find({ monitorId: monitor._id })
.sort({
createdAt: -1,
})
.limit(limit);
return { ...monitor.toObject(), checks };
} else {
const checks = await Check.find({ monitorId: monitor._id }).sort({
createdAt: -1,
});
return { ...monitor.toObject(), checks };
const checksQuery = { monitorId: monitor._id };
if (status !== undefined) {
checksQuery.status = status;
}
})
);
return monitorsWithChecks;
} catch (error) {
throw error;
}
};
let model =
monitor.type === "http" || monitor.type === "ping"
? Check
: PageSpeedCheck;
/**
* Get monitors by UserID
* @async
* @param {Express.Request} req
* @param {Express.Response} res
* @returns {Promise<Monitor>}
* @throws {Error}
*/
const getMonitorByIdForIncidents = async (req, res, next) => {
try {
const monitor = await Monitor.findById(req.params.monitorId);
const checks = await Check.find({
monitorId: monitor._id,
status: false,
}).sort({
createdAt: 1,
});
const monitorWithChecks = { ...monitor.toObject(), checks };
return monitorWithChecks;
} catch (error) {
throw error;
}
};
/**
* Get monitors by UserID
* @async
* @param {Express.Request} req
* @param {Express.Response} res
* @returns {Promise<Array<Monitor>>}
* @throws {Error}
*/
const getMonitorsByUserIdForIncidents = async (req, res) => {
try {
const monitors = await Monitor.find({ userId: req.params.userId });
// Map each monitor to include its associated checks
const monitorsWithChecks = await Promise.all(
monitors.map(async (monitor) => {
const checks = await Check.find({
monitorId: monitor._id,
status: false,
}).sort({
createdAt: 1,
});
// Checks are order newest -> oldest
const checks = await model
.find(checksQuery)
.sort({
createdAt: sortOrder,
})
.limit(limit);
return { ...monitor.toObject(), checks };
})
);
@@ -228,8 +224,6 @@ module.exports = {
getAllMonitors,
getMonitorById,
getMonitorsByUserId,
getMonitorByIdForIncidents,
getMonitorsByUserIdForIncidents,
createMonitor,
deleteMonitor,
deleteAllMonitors,
@@ -14,7 +14,7 @@ const PageSpeedCheck = require("../../../models/PageSpeedCheck");
*/
const createPageSpeedCheck = async (pageSpeedCheckData) => {
try {
const pageSpeedCheck = await new pageSpeedCheck({
const pageSpeedCheck = await new PageSpeedCheck({
...pageSpeedCheckData,
}).save();
return pageSpeedCheck;
-9
View File
@@ -7,15 +7,6 @@ router.get("/", monitorController.getAllMonitors);
router.get("/:monitorId", monitorController.getMonitorById);
router.get("/user/:userId", monitorController.getMonitorsByUserId);
router.get(
"/incidents/:monitorId",
monitorController.getMonitorByIdForIncidents
);
router.get(
"/incidents/user/:userId",
monitorController.getMonitorsByUserIdForIncidents
);
router.post("/", monitorController.createMonitor);
router.post(
"/delete/:monitorId",
+1
View File
@@ -189,6 +189,7 @@ class NetworkService {
const insertedCheck = await writeToDB(data);
return insertedCheck.status;
} catch (error) {
console.log(error);
logger.error(`Error wrtiting check for ${data.monitorId}`, {
service: this.SERVICE_NAME,
monitorId: data.monitorId,
+22 -3
View File
@@ -118,14 +118,32 @@ const inviteVerifciationBodyValidation = joi.object({
// Monitors
//****************************************
const getMonitorByIdValidation = joi.object({
const getMonitorByIdParamValidation = joi.object({
monitorId: joi.string().required(),
});
const getMonitorByIdQueryValidation = joi.object({
status: joi.boolean(),
sortOrder: joi.string().valid("asc", "desc"),
limit: joi.number(),
});
const getMonitorsByUserIdValidation = joi.object({
userId: joi.string().required(),
});
const getMonitorsByUserIdQueryValidation = joi.object({
status: joi.boolean(),
sortOrder: joi.string().valid("asc", "desc"),
limit: joi.number(),
type: joi
.alternatives()
.try(
joi.string().valid("http", "ping", "pagespeed"),
joi.array().items(joi.string().valid("http", "ping", "pagespeed"))
),
});
const monitorValidation = joi.object({
_id: joi.string(),
userId: joi.string().required(),
@@ -244,9 +262,10 @@ module.exports = {
inviteRoleValidation,
inviteBodyValidation,
inviteVerifciationBodyValidation,
getMonitorByIdValidation,
getMonitorByIdParamValidation,
getMonitorByIdQueryValidation,
getMonitorsByUserIdValidation,
monitorValidation,
getMonitorsByUserIdQueryValidation,
editMonitorBodyValidation,
editUserParamValidation,
editUserBodyValidation,