From f0beddbaa8f1597ba3079dc126312a52ced1a7b0 Mon Sep 17 00:00:00 2001 From: M M Date: Sun, 19 May 2024 22:40:55 -0700 Subject: [PATCH 1/6] Implemented the integrations component. --- Client/src/Components/Integrations/index.css | 0 Client/src/Components/Integrations/index.jsx | 24 ++++++++++++++++++++ Client/src/Pages/Home/index.jsx | 2 ++ 3 files changed, 26 insertions(+) create mode 100644 Client/src/Components/Integrations/index.css create mode 100644 Client/src/Components/Integrations/index.jsx diff --git a/Client/src/Components/Integrations/index.css b/Client/src/Components/Integrations/index.css new file mode 100644 index 000000000..e69de29bb diff --git a/Client/src/Components/Integrations/index.jsx b/Client/src/Components/Integrations/index.jsx new file mode 100644 index 000000000..030a79309 --- /dev/null +++ b/Client/src/Components/Integrations/index.jsx @@ -0,0 +1,24 @@ +import { Box, Button, Typography } from '@mui/material'; + +const Integrations = () => { + return ( + + + + + + Header + + + This is a paragraph that provides more detail about the header. + + + + + + ); +}; + +export default Integrations; diff --git a/Client/src/Pages/Home/index.jsx b/Client/src/Pages/Home/index.jsx index ff5cb454e..a1ad8e314 100644 --- a/Client/src/Pages/Home/index.jsx +++ b/Client/src/Pages/Home/index.jsx @@ -2,6 +2,7 @@ import DropdownTeamMember from "../../Components/DropdownTeamMember"; import Search from "../../Components/Search"; import "./index.css"; import DashboardMenu from "../../Components/DashboardMenu"; +import Integrations from "../../Components/Integrations"; const Home = () => { return ( @@ -10,6 +11,7 @@ const Home = () => { + ); }; From e7cd1ae6ab835f748bd097d866879051a02eaa5d Mon Sep 17 00:00:00 2001 From: Alex Holliday Date: Tue, 21 May 2024 11:23:39 -0700 Subject: [PATCH 2/6] Added create and delete monitor routes, Added JSDocs to MongoDB --- Server/controllers/monitorController.js | 73 +++++++++++++++++++- Server/db/FakeDb.js | 24 ++++++- Server/db/MongoDB.js | 90 +++++++++++++++++++++++-- Server/routes/monitorRoute.js | 3 + Server/validation/joi.js | 14 +++- 5 files changed, 191 insertions(+), 13 deletions(-) diff --git a/Server/controllers/monitorController.js b/Server/controllers/monitorController.js index 793542cbf..f694fdf71 100644 --- a/Server/controllers/monitorController.js +++ b/Server/controllers/monitorController.js @@ -1,8 +1,11 @@ const { - getMonitorsByIdValidation, + getMonitorByIdValidation, getMonitorsByUserIdValidation, + createMonitorValidation, } = require("../validation/joi"); + const logger = require("../utils/logger"); +const Monitor = require("../models/Monitor"); /** * Returns all monitors @@ -31,7 +34,7 @@ const getAllMonitors = async (req, res) => { * @throws {Error} */ const getMonitorById = async (req, res) => { - const { error } = getMonitorsByIdValidation.validate(req.params); + const { error } = getMonitorByIdValidation.validate(req.params); if (error) { return res .status(400) @@ -82,4 +85,68 @@ const getMonitorsByUserId = async (req, res) => { } }; -module.exports = { getAllMonitors, getMonitorById, getMonitorsByUserId }; +/** + * Creates a new monitor + * @async + * @param {Express.Request} req + * @param {Express.Response} res + * @returns {Promise} + * @throws {Error} + */ + +const createMonitor = async (req, res) => { + const { error } = createMonitorValidation.validate(req.body); + if (error) { + return res + .status(400) + .json({ success: false, msg: error.details[0].message }); + } + + try { + const monitor = await req.db.createMonitor(req, res); + return res + .status(200) + .json({ success: true, msg: "Monitor created", data: monitor }); + } catch (error) { + logger.error(error.message, { service: "monitor" }); + return res.status(500).json({ success: false, msg: error.message }); + } +}; + +// Delete Monitor +/** + * Delete a monitor by ID + * @async + * @param {Express.Request} req + * @param {Express.Response} res + * @returns {Promise} + * @throws {Error} + */ + +const deleteMonitor = async (req, res) => { + const { error } = getMonitorByIdValidation.validate(req.params); + if (error) { + return res + .status(400) + .json({ success: false, msg: error.details[0].message }); + } + try { + const monitor = await req.db.deleteMonitor(req, res); + return res + .status(200) + .json({ success: true, msg: "Monitor deleted", data: monitor }); + } catch (error) { + logger.error(error.message, { service: "monitor" }); + return res.status(500).json({ success: false, msg: error.message }); + } +}; + +// Edit Monitor + +module.exports = { + getAllMonitors, + getMonitorById, + getMonitorsByUserId, + createMonitor, + deleteMonitor, +}; diff --git a/Server/db/FakeDb.js b/Server/db/FakeDb.js index a21b66ffd..1510bc55e 100644 --- a/Server/db/FakeDb.js +++ b/Server/db/FakeDb.js @@ -24,7 +24,7 @@ const Monitor = require("../models/Monitor"); const UserModel = require("../models/user"); const bcrypt = require("bcrypt"); -const FAKE_MONITOR_DATA = []; +let FAKE_MONITOR_DATA = []; const USERS = []; for (let i = 0; i < 10; i++) { @@ -99,10 +99,28 @@ const getMonitorsByUserId = async (userId) => { if (userMonitors.length === 0) { throw new Error(`Monitors for user ${userId} not found`); } - return userMonitors; }; +const createMonitor = async (req, res) => { + const monitor = new Monitor(req.body); + FAKE_MONITOR_DATA.push(monitor); + return monitor; +}; + +const deleteMonitor = async (req, res) => { + const monitorId = req.params.monitorId; + try { + const monitor = getMonitorById(monitorId); + FAKE_MONITOR_DATA = FAKE_MONITOR_DATA.filter((monitor) => { + return monitor.id !== monitorId; + }); + return monitor; + } catch (error) { + throw error; + } +}; + module.exports = { connect, insertUser, @@ -110,4 +128,6 @@ module.exports = { getAllMonitors, getMonitorById, getMonitorsByUserId, + createMonitor, + deleteMonitor, }; diff --git a/Server/db/MongoDB.js b/Server/db/MongoDB.js index b6a126ab8..7546cda37 100644 --- a/Server/db/MongoDB.js +++ b/Server/db/MongoDB.js @@ -1,7 +1,6 @@ const Monitor = require("../models/Monitor"); const mongoose = require("mongoose"); const UserModel = require("../models/user"); -const user = require("../models/user"); const connect = async () => { try { @@ -13,6 +12,14 @@ const connect = async () => { } }; +/** + * Insert a User + * @async + * @param {Express.Request} req + * @param {Express.Response} res + * @returns {Promise} + * @throws {Error} + */ const insertUser = async (req, res) => { try { const newUser = new UserModel({ ...req.body }); @@ -23,8 +30,16 @@ const insertUser = async (req, res) => { } }; -// Gets a user by Email. Not sure if we'll ever need this except for login. -// If not needed except for login, we can move password comparison here +/** + * Get User by Email + * Gets a user by Email. Not sure if we'll ever need this except for login. + * If not needed except for login, we can move password comparison here + * @async + * @param {Express.Request} req + * @param {Express.Response} res + * @returns {Promise} + * @throws {Error} + */ const getUserByEmail = async (req, res) => { try { // Returns null if no user is found @@ -36,7 +51,14 @@ const getUserByEmail = async (req, res) => { } }; -// Gets all monitors +/** + * Get all monitors + * @async + * @param {Express.Request} req + * @param {Express.Response} res + * @returns {Promise>} + * @throws {Error} + */ const getAllMonitors = async (req, res) => { try { const monitors = await Monitor.find(); @@ -46,7 +68,14 @@ const getAllMonitors = async (req, res) => { } }; -// Get a monitor by ID +/** + * Get a monitor by ID + * @async + * @param {Express.Request} req + * @param {Express.Response} res + * @returns {Promise} + * @throws {Error} + */ const getMonitorById = async (req, res) => { try { const monitor = await Monitor.findById(req.params.monitorId); @@ -56,7 +85,14 @@ const getMonitorById = async (req, res) => { } }; -// Gets a monitor by user ID +/** + * Get monitors by UserID + * @async + * @param {Express.Request} req + * @param {Express.Response} res + * @returns {Promise>} + * @throws {Error} + */ const getMonitorsByUserId = async (req, res) => { try { const monitors = await Monitor.find({ userId: req.params.userId }); @@ -66,6 +102,46 @@ const getMonitorsByUserId = async (req, res) => { } }; +/** + * Create a monitor + * @async + * @param {Express.Request} req + * @param {Express.Response} res + * @returns {Promise} + * @throws {Error} + */ +const createMonitor = async (req, res) => { + try { + const monitor = new Monitor({ ...req.body }); + await monitor.save(); + return monitor; + } catch (error) { + throw error; + } +}; + +// Delete Monitor +/** + * Delete a monitor by ID + * @async + * @param {Express.Request} req + * @param {Express.Response} res + * @returns {Promise} + * @throws {Error} + */ +const deleteMonitor = async (req, res) => { + const monitorId = req.params.monitorId; + try { + const monitor = await Monitor.findByIdAndDelete(monitorId); + if (!monitor) { + throw new Error(`Monitor with id ${monitorId} not found`); + } + return monitor; + } catch (error) { + throw error; + } +}; + module.exports = { connect, insertUser, @@ -73,4 +149,6 @@ module.exports = { getAllMonitors, getMonitorById, getMonitorsByUserId, + createMonitor, + deleteMonitor, }; diff --git a/Server/routes/monitorRoute.js b/Server/routes/monitorRoute.js index e12982905..07c20e13f 100644 --- a/Server/routes/monitorRoute.js +++ b/Server/routes/monitorRoute.js @@ -1,7 +1,10 @@ const router = require("express").Router(); const monitorController = require("../controllers/monitorController"); + router.get("/", monitorController.getAllMonitors); router.get("/:monitorId", monitorController.getMonitorById); router.get("/user/:userId", monitorController.getMonitorsByUserId); +router.post("/", monitorController.createMonitor); +router.post("/delete/:monitorId", monitorController.deleteMonitor); module.exports = router; diff --git a/Server/validation/joi.js b/Server/validation/joi.js index 874daff93..61c8ffeb3 100644 --- a/Server/validation/joi.js +++ b/Server/validation/joi.js @@ -12,7 +12,7 @@ const registerValidation = joi.object({ password: joi.string().min(8).required(), }); -const getMonitorsByIdValidation = joi.object({ +const getMonitorByIdValidation = joi.object({ monitorId: joi.string().required(), }); @@ -20,9 +20,19 @@ const getMonitorsByUserIdValidation = joi.object({ userId: joi.string().required(), }); +const createMonitorValidation = joi.object({ + userId: joi.string().required(), + name: joi.string().required(), + description: joi.string().required(), + url: joi.string().uri().required(), + isActive: joi.boolean(), + interval: joi.number(), +}); + module.exports = { loginValidation, registerValidation, - getMonitorsByIdValidation, + getMonitorByIdValidation, getMonitorsByUserIdValidation, + createMonitorValidation, }; From face3339cd1f319b6f2b111d52517207fe56dc62 Mon Sep 17 00:00:00 2001 From: Alex Holliday Date: Tue, 21 May 2024 11:28:36 -0700 Subject: [PATCH 3/6] Updated getAllMonitors to only return active monitors --- Server/db/MongoDB.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Server/db/MongoDB.js b/Server/db/MongoDB.js index 7546cda37..d9be30f15 100644 --- a/Server/db/MongoDB.js +++ b/Server/db/MongoDB.js @@ -61,7 +61,7 @@ const getUserByEmail = async (req, res) => { */ const getAllMonitors = async (req, res) => { try { - const monitors = await Monitor.find(); + const monitors = await Monitor.find({ isActive: true }); return monitors; } catch (error) { throw error; From c4dc57b9be8d3c2ab0adcfc405756ae143b1478d Mon Sep 17 00:00:00 2001 From: Alex Holliday Date: Tue, 21 May 2024 13:52:28 -0700 Subject: [PATCH 4/6] Updated status codes, added edit monitor endpoint --- Server/controllers/monitorController.js | 60 ++++++++++++++++++----- Server/db/FakeDb.js | 33 +++++++------ Server/db/MongoDB.js | 27 ++++++++++- Server/models/Monitor.js | 63 ++++++++++++------------- Server/routes/monitorRoute.js | 1 + Server/validation/joi.js | 5 +- 6 files changed, 126 insertions(+), 63 deletions(-) diff --git a/Server/controllers/monitorController.js b/Server/controllers/monitorController.js index f694fdf71..8b2f72ab1 100644 --- a/Server/controllers/monitorController.js +++ b/Server/controllers/monitorController.js @@ -1,7 +1,7 @@ const { getMonitorByIdValidation, getMonitorsByUserIdValidation, - createMonitorValidation, + monitorValidation, } = require("../validation/joi"); const logger = require("../utils/logger"); @@ -37,7 +37,7 @@ const getMonitorById = async (req, res) => { const { error } = getMonitorByIdValidation.validate(req.params); if (error) { return res - .status(400) + .status(422) .json({ success: false, msg: error.details[0].message }); } @@ -63,7 +63,7 @@ const getMonitorsByUserId = async (req, res) => { const { error } = getMonitorsByUserIdValidation.validate(req.params); if (error) { return res - .status(400) + .status(422) .json({ success: false, msg: error.details[0].message }); } @@ -95,17 +95,17 @@ const getMonitorsByUserId = async (req, res) => { */ const createMonitor = async (req, res) => { - const { error } = createMonitorValidation.validate(req.body); + const { error } = monitorValidation.validate(req.body); if (error) { return res - .status(400) + .status(422) .json({ success: false, msg: error.details[0].message }); } try { const monitor = await req.db.createMonitor(req, res); return res - .status(200) + .status(201) .json({ success: true, msg: "Monitor created", data: monitor }); } catch (error) { logger.error(error.message, { service: "monitor" }); @@ -113,7 +113,6 @@ const createMonitor = async (req, res) => { } }; -// Delete Monitor /** * Delete a monitor by ID * @async @@ -127,21 +126,57 @@ const deleteMonitor = async (req, res) => { const { error } = getMonitorByIdValidation.validate(req.params); if (error) { return res - .status(400) + .status(422) .json({ success: false, msg: error.details[0].message }); } try { const monitor = await req.db.deleteMonitor(req, res); - return res - .status(200) - .json({ success: true, msg: "Monitor deleted", data: monitor }); + /** + * TODO + * We should remove all checks and alerts associated with this monitor + * when it is deleted so there is no orphaned data + * We also need to make sure to stop all running services for this monitor + */ + return res.status(204).json({ success: true, msg: "Monitor deleted" }); } catch (error) { logger.error(error.message, { service: "monitor" }); return res.status(500).json({ success: false, msg: error.message }); } }; -// Edit Monitor +/** + * Edit a monitor by ID + * @async + * @param {Express.Request} req + * @param {Express.Response} res + * @returns {Promise} + * @throws {Error} + */ +const editMonitor = async (req, res) => { + let paramError = getMonitorByIdValidation.validate(req.params); + if (paramError.error) { + return res + .status(422) + .json({ success: false, msg: paramError.error.details[0].message }); + } + + let { error } = monitorValidation.validate(req.body); + if (error) { + return res + .status(422) + .json({ success: false, msg: error.details[0].message }); + } + + try { + const editedMonitor = await req.db.editMonitor(req, res); + return res + .status(200) + .json({ success: true, msg: "Monitor edited", data: editedMonitor }); + } catch (error) { + logger.error(error.message, { service: "monitor" }); + return res.status(500).json({ success: false, msg: error.message }); + } +}; module.exports = { getAllMonitors, @@ -149,4 +184,5 @@ module.exports = { getMonitorsByUserId, createMonitor, deleteMonitor, + editMonitor, }; diff --git a/Server/db/FakeDb.js b/Server/db/FakeDb.js index 1510bc55e..b2abd919e 100644 --- a/Server/db/FakeDb.js +++ b/Server/db/FakeDb.js @@ -27,21 +27,6 @@ const bcrypt = require("bcrypt"); let FAKE_MONITOR_DATA = []; const USERS = []; -for (let i = 0; i < 10; i++) { - FAKE_MONITOR_DATA.push( - new Monitor({ - userId: i % 2 === 0 ? 1 : 2, - name: `Monitor ${i}`, - description: `Description for Monitor ${i}`, - url: `https://monitor${i}.com`, - isActive: true, - interval: 60000, - updated_at: new Date(), - created_at: new Date(), - }) - ); -} - const connect = async () => { try { await console.log("Connected to FakeDB"); @@ -104,6 +89,8 @@ const getMonitorsByUserId = async (userId) => { const createMonitor = async (req, res) => { const monitor = new Monitor(req.body); + monitor.createdAt = Date.now(); + monitor.updatedAt = Date.now(); FAKE_MONITOR_DATA.push(monitor); return monitor; }; @@ -121,6 +108,21 @@ const deleteMonitor = async (req, res) => { } }; +const editMonitor = async (req, res) => { + const monitorId = req.params.monitorId; + const idx = FAKE_MONITOR_DATA.findIndex((monitor) => { + return monitor._id.toString() === monitorId; + }); + const oldMonitor = FAKE_MONITOR_DATA[idx]; + const editedMonitor = new Monitor({ ...req.body }); + editedMonitor._id = oldMonitor._id; + editedMonitor.userId = oldMonitor.userId; + editedMonitor.updatedAt = Date.now(); + editedMonitor.createdAt = oldMonitor.createdAt; + FAKE_MONITOR_DATA[idx] = editedMonitor; + return FAKE_MONITOR_DATA[idx]; +}; + module.exports = { connect, insertUser, @@ -130,4 +132,5 @@ module.exports = { getMonitorsByUserId, createMonitor, deleteMonitor, + editMonitor, }; diff --git a/Server/db/MongoDB.js b/Server/db/MongoDB.js index d9be30f15..256e7f02c 100644 --- a/Server/db/MongoDB.js +++ b/Server/db/MongoDB.js @@ -61,7 +61,7 @@ const getUserByEmail = async (req, res) => { */ const getAllMonitors = async (req, res) => { try { - const monitors = await Monitor.find({ isActive: true }); + const monitors = await Monitor.find(); return monitors; } catch (error) { throw error; @@ -120,7 +120,6 @@ const createMonitor = async (req, res) => { } }; -// Delete Monitor /** * Delete a monitor by ID * @async @@ -142,6 +141,29 @@ const deleteMonitor = async (req, res) => { } }; +/** + * Edit a monitor by ID + * @async + * @param {Express.Request} req + * @param {Express.Response} res + * @returns {Promise} + * @throws {Error} + */ +const editMonitor = async (req, res) => { + const candidateId = req.params.monitorId; + const candidateMonitor = req.body; + try { + const editedMonitor = await Monitor.findByIdAndUpdate( + candidateId, + candidateMonitor, + { new: true } + ); + return editedMonitor; + } catch (error) { + throw error; + } +}; + module.exports = { connect, insertUser, @@ -151,4 +173,5 @@ module.exports = { getMonitorsByUserId, createMonitor, deleteMonitor, + editMonitor, }; diff --git a/Server/models/Monitor.js b/Server/models/Monitor.js index 513d4e2be..4aaad1497 100644 --- a/Server/models/Monitor.js +++ b/Server/models/Monitor.js @@ -1,37 +1,36 @@ const mongoose = require("mongoose"); -const MonitorSchema = mongoose.Schema({ - userId: { - type: String, +const MonitorSchema = mongoose.Schema( + { + userId: { + type: mongoose.Schema.Types.ObjectId, + ref: "User", + immutable: true, + }, + name: { + type: String, + required: true, + }, + description: { + type: String, + }, + url: { + type: String, + required: true, + }, + isActive: { + type: Boolean, + default: true, + }, + interval: { + // in milliseconds + type: Number, + default: 60000, + }, }, - name: { - type: String, - required: true, - }, - description: { - type: String, - }, - url: { - type: String, - required: true, - }, - isActive: { - type: Boolean, - default: true, - }, - interval: { - // in milliseconds - type: Number, - default: 60000, - }, - updated_at: { - type: Date, - default: Date.now, - }, - created_at: { - type: Date, - default: Date.now, - }, -}); + { + timestamps: true, + } +); module.exports = mongoose.model("Monitor", MonitorSchema); diff --git a/Server/routes/monitorRoute.js b/Server/routes/monitorRoute.js index 07c20e13f..5f2ce4bb5 100644 --- a/Server/routes/monitorRoute.js +++ b/Server/routes/monitorRoute.js @@ -7,4 +7,5 @@ router.get("/user/:userId", monitorController.getMonitorsByUserId); router.post("/", monitorController.createMonitor); router.post("/delete/:monitorId", monitorController.deleteMonitor); +router.post("/edit/:monitorId", monitorController.editMonitor); module.exports = router; diff --git a/Server/validation/joi.js b/Server/validation/joi.js index 61c8ffeb3..ecc19253e 100644 --- a/Server/validation/joi.js +++ b/Server/validation/joi.js @@ -20,7 +20,8 @@ const getMonitorsByUserIdValidation = joi.object({ userId: joi.string().required(), }); -const createMonitorValidation = joi.object({ +const monitorValidation = joi.object({ + _id: joi.string(), userId: joi.string().required(), name: joi.string().required(), description: joi.string().required(), @@ -34,5 +35,5 @@ module.exports = { registerValidation, getMonitorByIdValidation, getMonitorsByUserIdValidation, - createMonitorValidation, + monitorValidation, }; From 4f934c0d6936102657389a816d8b90f7670a10b3 Mon Sep 17 00:00:00 2001 From: Alex Holliday Date: Tue, 21 May 2024 13:58:29 -0700 Subject: [PATCH 5/6] Accidentally removed isActive filter from getAll, replaced it --- Server/db/MongoDB.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Server/db/MongoDB.js b/Server/db/MongoDB.js index 256e7f02c..8fcc1dced 100644 --- a/Server/db/MongoDB.js +++ b/Server/db/MongoDB.js @@ -61,7 +61,7 @@ const getUserByEmail = async (req, res) => { */ const getAllMonitors = async (req, res) => { try { - const monitors = await Monitor.find(); + const monitors = await Monitor.find({ isActive: true }); return monitors; } catch (error) { throw error; From aa000bda8bb710a8aa413747e9526a664f6eb271 Mon Sep 17 00:00:00 2001 From: M M Date: Tue, 21 May 2024 16:38:11 -0700 Subject: [PATCH 6/6] Refactored the code and addressed the comments. --- Client/src/Components/Integrations/index.jsx | 48 ++++++++++++++++---- 1 file changed, 40 insertions(+), 8 deletions(-) diff --git a/Client/src/Components/Integrations/index.jsx b/Client/src/Components/Integrations/index.jsx index 030a79309..b0e00bdfe 100644 --- a/Client/src/Components/Integrations/index.jsx +++ b/Client/src/Components/Integrations/index.jsx @@ -1,22 +1,54 @@ -import { Box, Button, Typography } from '@mui/material'; +import { Box, Typography, useTheme } from '@mui/material'; +import Button from '../Button'; +/** + * Integrations component + * @returns {JSX.Element} + */ const Integrations = () => { + const theme = useTheme(); + return ( - - + + - + Header - + This is a paragraph that provides more detail about the header. - +