diff --git a/_apidoc.js b/_apidoc.js index 1309a25..ff97976 100644 --- a/_apidoc.js +++ b/_apidoc.js @@ -666,6 +666,35 @@ * attainment as per Bloom's Taxanomy (L1-L6). */ +// ------------------------------------------------------------------------------------------ +// Group. +// ------------------------------------------------------------------------------------------ + +/** + * @api {post} /group/add Add Group + * @apiName AddGroup + * @apiGroup Group + * @apiDescription Add a new group. + * + * @apiBody {String} title Group title. + * @apiBody {ObjectId[]} students Array of student ObjectIDs. + * + * @apiSuccess {String} res Response message. + * @apiError (Error 500) GroupAddError Error while adding the group + * + * @apiSuccessExample Success-Response: + * HTTP/1.1 200 OK + * { + * "res": "added group Example Group" + * } + * + * @apiErrorExample Error-Response: + * HTTP/1.1 500 Internal Server Error + * { + * "err": "Error while inserting in DB" + * } + */ + // ------------------------------------------------------------------------------------------ // Semester // ------------------------------------------------------------------------------------------ @@ -757,9 +786,6 @@ * @apiError (Error 500) DatabaseError Error while inserting in the database. * * @apiDescription Adds a new Activity to the system. - */ - -/** * * @apiSuccessExample Success-Response: * HTTP/1.1 200 OK @@ -774,6 +800,45 @@ * } */ +/** + * @api {delete} /group/delete/:id Delete Group + * @apiName DeleteGroup + * @apiGroup Group + * + * @apiParam {ObjectId} id The ObjectID of the group to delete. + * + * @apiSuccess {String} res Success message indicating the deletion. + * @apiError (Error 500) GroupDeleteError Error while deleting the group + * + */ + +/** + * @api {post} /group/update/:id Update Group Details + * @apiName UpdateGroup + * @apiGroup Group + * @apiDescription Update existing group details. + * + * @apiParam {ObjectId} id The ObjectID of the group to update. + * @apiBody {String} [title] Group title. + * @apiBody {ObjectId[]} [students] Array of student ObjectIDs. + * + * @apiSuccess {String} res Group updated. + * @apiError (Error 500) GroupUpdateError Error in updating database + * + */ + +/** + * @api {get} /group/list Get Group List + * @apiName GetGroupList + * @apiGroup Group + * + * @apiQuery {String} [title] Title of the group. + * + * @apiSuccess {Group[]} res Array of filtered group documents. + * @apiSuccess {ObjectId} group._id ObjectID of the group document in the database. + * @apiSuccess {String} group.title Title of the group. + * @apiSuccess {ObjectId[]} group.students Array of student ObjectIDs in the group. + */ /** * @api {delete} /timetable/delete/:timetableId Delete Timetable * @apiName DeleteTimetable diff --git a/app.js b/app.js index 4d6c99c..2f34bee 100644 --- a/app.js +++ b/app.js @@ -23,6 +23,7 @@ import moduleRouter from "#routes/module"; import facultyRouter from "#routes/faculty"; import { identifyUser } from "#middleware/identifyUser"; import departmentRouter from "#routes/department"; +import groupRouter from "#routes/group"; const app = express(); const currDirName = dirname(fileURLToPath(import.meta.url)); @@ -56,6 +57,7 @@ app.use("/timetable", timetableRouter); app.use("/department", departmentRouter); app.use("/coursework", courseworkRouter); app.use("/module", moduleRouter); +app.use("/group", groupRouter); app.use("/semester", semesterRouter); app.use("/faculty", facultyRouter); export default app; diff --git a/controller/group.js b/controller/group.js new file mode 100644 index 0000000..a0284fc --- /dev/null +++ b/controller/group.js @@ -0,0 +1,54 @@ +import { + createGroup, deleteGroupById, groupList, updateGroupById, +} from "#services/group"; +import { logger } from "#util"; + +async function addGroup(req, res) { + const { + title, student, + } = req.body; + try { + const group = await createGroup(title, student); + res.json({ res: `added group ${group.id}`, id: group.id }); + } catch (error) { + logger.error("Error while inserting", error); + res.status(500); + res.json({ err: "Error while inserting in DB" }); + } +} + +async function updateGroup(req, res) { + const { id } = req.params; + const { + ...data + } = req.body; + try { + await updateGroupById(id, data); + res.json({ res: `updated group with id ${id}` }); + } catch (error) { + logger.error("Error while updating", error); + res.status(500); + res.json({ err: "Error while updaing in DB" }); + } +} + +async function getGroup(req, res) { + const filter = req.query; + const group = await groupList(filter); + res.json({ res: group }); +} + +async function deleteGroup(req, res) { + const { id } = req.params; + try { + await deleteGroupById(id); + res.json({ res: `Deleted group with ID ${id}` }); + } catch (error) { + logger.error("Error while deleting", error); + res.status(500).json({ error: "Error while deleting from DB" }); + } +} + +export default { + addGroup, deleteGroup, getGroup, updateGroup, +}; diff --git a/models/group.js b/models/group.js index 2f9f9f4..57cfed1 100644 --- a/models/group.js +++ b/models/group.js @@ -19,7 +19,7 @@ async function read(filter, limit = 1) { } async function update(filter, updateObject, options = { multi: true }) { - const updateResult = await Group.updateManyupdateMany(filter, { $set: updateObject }, options); + const updateResult = await Group.updateMany(filter, { $set: updateObject }, options); return updateResult.acknowledged; } async function remove(groupId) { diff --git a/routes/group.js b/routes/group.js new file mode 100644 index 0000000..e9f20a5 --- /dev/null +++ b/routes/group.js @@ -0,0 +1,10 @@ +import express from "express"; +import groupController from "#controller/group"; + +const router = express.Router(); +router.post("/add", groupController.addGroup); +router.get("/list", groupController.getGroup); +router.post("/update/:id", groupController.updateGroup); +router.delete("/delete/:id", groupController.deleteGroup); + +export default router; diff --git a/services/group.js b/services/group.js new file mode 100644 index 0000000..d02ff62 --- /dev/null +++ b/services/group.js @@ -0,0 +1,33 @@ +import Group from "#models/group"; +import databaseError from "#error/database"; + +export async function createGroup(title, student) { + const newGroup = await Group.create({ + title, student, + }); + if (newGroup.title === title) { + return newGroup; + } + throw new databaseError.DataEntryError("group"); +} + +export async function updateGroupById(id, data) { + const updated = await Group.update({ _id: id }, data); + if (updated) { + return updated; + } + throw new databaseError.DataEntryError("group"); +} + +export async function groupList(filter) { + const groups = await Group.read(filter, 0); + return groups; +} + +export async function deleteGroupById(groupId) { + const deleted = await Group.remove({ _id: groupId }); + if (deleted) { + return deleted; + } + throw new databaseError.DataDeleteError("group"); +} diff --git a/test/routes/group.test.js b/test/routes/group.test.js new file mode 100644 index 0000000..4770ef9 --- /dev/null +++ b/test/routes/group.test.js @@ -0,0 +1,84 @@ +import { jest } from "@jest/globals"; // eslint-disable-line import/no-extraneous-dependencies +import request from "supertest"; +import app from "#app"; +import connector from "#models/databaseUtil"; +import groupModel from "#models/group"; + +jest.mock("#util"); + +let server; +let agent; + +beforeAll((done) => { + server = app.listen(null, () => { + agent = request.agent(server); + connector.set("debug", false); + done(); + }); +}); + +function cleanUp(callback) { + groupModel + .remove({ + id: "6500594e2b7b532006c073dd", + }) + .then(() => { + connector.disconnect((DBerr) => { + if (DBerr) console.log("Database disconnect error: ", DBerr); + server.close((serverErr) => { + if (serverErr) console.log(serverErr); + callback(); + }); + }); + }); +} + +afterAll((done) => { + cleanUp(done); +}); + +describe("group API", () => { + it("should create group", async () => { + const response = await agent.post("/group/add").send({ + title: "Group 1", + student: "64fdc67feca8a69f01b33614", + }); + expect(response.headers["content-type"]).toMatch(/json/); + expect(response.status).toBe(200); + expect(response.body.res).toMatch(/added group/); + }); + + describe("after adding group", () => { + let id; + beforeEach(async () => { + id = await agent.post("/group/add").send({ + title: "Group 1", + student: "64fdc67feca8a69f01b33614", + }); + id = JSON.parse(id.res.text).id; + }); + + afterEach(async () => { + await groupModel.remove({ + id: "6500594e2b7b532006c073dd", + }); + }); + + it("should read group", async () => { + const response = await agent + .get("/group/list") + .send({ name: "Building A" }); + expect(response.status).toBe(200); + expect(response.body.res).toBeDefined(); + }); + + it("should update group", async () => { + const response = await agent + .post(`/group/update/${id}`) + .send({ title: "Group 1" }, { title: "Group 2" }); + expect(response.headers["content-type"]).toMatch(/json/); + expect(response.status).toBe(200); + expect(response.body.res).toMatch(/updated group/); + }); + }); +});