diff --git a/src/resources/activity/activityControllers.js b/src/resources/activity/activityControllers.js index cec720f870970e1a660b1ca228154cb9139c277b..f134126bd95ea794573236fcc880aaf643e4bb8d 100644 --- a/src/resources/activity/activityControllers.js +++ b/src/resources/activity/activityControllers.js @@ -1,10 +1,207 @@ const { crudControllers } = require('../../utils/crud') const { Activity } = require('./activityModel') +const { User } = require('../user/userModel') +const { Attendance } = require('../attendance/attendanceModel') +const { Comment } = require('../comment/commentModel') +const { omit, pick } = require('lodash') -exports.default = crudControllers(Activity, [ +const pickedKeys = [ '_id', 'title', 'description', 'date', 'type', -]) + 'createdAt', + 'attendance', + 'comment', +] + +exports.default = crudControllers(Activity, pickedKeys) + +exports.default.createOne = async (req, res) => { + try { + // Invalid Date provided + if (!req.body.date || Date.parse(req.body.date) < new Date().getTime()) + return res.status(403).json({ messages: ['Invalid date'] }) + + let activity = await Activity.create({ ...req.body }) + + // Create Initial participants + if ( + !( + req.body.initialParticipants != null && + req.body.initialParticipants == false + ) + ) { + let acceptedUsers = await User.find({ role: 'accepted' }).lean().exec() + acceptedUsers = acceptedUsers.map(function getUserSchaccs(user) { + return { schacc: user.schacc } + }) + + let attendances = activity.attendance + for await (let user of acceptedUsers) { + const newAttendance = await Attendance.create({ + activity: activity._id, + user: user.schacc, + }) + attendances.push(newAttendance._id) + } + + await Activity.findOneAndUpdate( + { _id: activity._id }, + { attendance: attendances }, + { new: true } + ) + .lean() + .exec() + } + + await activity + .populate({ + path: 'comment', + select: '_id creator text createdAt', + }) + .populate({ + path: 'attendance', + select: '_id user state', + }) + .execPopulate() + + // conver to JS Object to overwrite .creator + let objActivity = activity.toObject() + objActivity.creator = objActivity._creator + + res + .status(200) + .json({ + data: pick(objActivity, pickedKeys), + }) + .end() + } catch (err) { + if (err.name == 'ValidationError') { + // Throwed by Mongoose + let messages = [] + for (field in err.errors) { + messages.push(err.errors[field].message) + } + return res.status(422).json({ messages }) + } + console.error(err) + return res.status(500).json({ message: err.message }) + } +} + +exports.default.getOne = async (req, res) => { + try { + const activity = await Activity.findOne({ _id: req.params.id }) + .populate({ + path: 'comment', + select: '_id creator text createdAt', + }) + .populate({ + path: 'attendance', + select: '_id user state', + }) + .select('-__v') + .lean() + .exec() + + if (!activity) + return res.status(404).json({ messages: ['No such activity.'] }) + + return res.status(200).json({ data: pick(activity, pickedKeys) }) + } catch (err) { + console.error(err) + return res.status(500).json({ message: err.message }) + } +} + +exports.default.getMany = async (req, res) => { + try { + const activity = await Activity.find() + .populate({ + path: 'comment', + select: '_id creator text createdAt', + }) + .populate({ + path: 'attendance', + select: '_id user state', + }) + .select('-__v') + .lean() + .exec() + + if (!activity) + return res.status(404).json({ message: 'Activity not found!' }) + + res.status(200).json({ + data: activity.map(function pickKeys(doc) { + return pick(doc, pickedKeys) + }), + }) + } catch (err) { + console.error(err) + res.status(500).json({ message: err.message }) + } +} + +exports.default.removeOne = async (req, res) => { + try { + const activity = await Activity.findByIdAndDelete({ + _id: req.params.id, + }) + .populate({ + path: 'comment', + select: '_id creator text createdAt', + }) + .select('-__v') + .lean() + .exec() + + if (!activity) { + return res.status(404).json({ message: 'Activity not found!' }) + } + + await Attendance.deleteMany({ + _id: activity.attendance.map(function getAttendanceIds(element) { + return element._id + }), + }) + + await Comment.deleteMany({ + _id: activity.comment.map(function getCommentIds(element) { + return element._id + }), + }) + + return res.status(200).json({ data: pick(activity, pickedKeys) }) + } catch (err) { + console.error(err) + return res.status(500).json({ message: err.message }) + } +} + +exports.default.updateOne = async (req, res) => { + try { + const activity = await Activity.findOneAndUpdate( + { _id: req.params.id }, + omit(req.body, ['attendance', 'comment']), + { new: true } + ) + .populate({ + path: 'comment', + select: '_id creator text createdAt', + }) + .select('-__v') + .lean() + .exec() + + if (!activity) + return res.status(404).json({ message: 'Activity not found!' }) + + res.status(200).json({ data: pick(activity, pickedKeys) }) + } catch (err) { + console.error(err) + res.status(500).json({ message: err.message }) + } +} diff --git a/src/resources/activity/activityModel.js b/src/resources/activity/activityModel.js index fa90dd75f31c2d2f1892b761ae7d019ecbb805e9..36ba685103e811b9fcff0e8482f6b490e9d9f71d 100644 --- a/src/resources/activity/activityModel.js +++ b/src/resources/activity/activityModel.js @@ -36,9 +36,6 @@ const ActivitySchema = new mongoose.Schema( { timestamps: true } ) -// Careful with the docs, there are some deprecated ones -// https://mongoosejs.com/docs/guide.html - const Activity = mongoose.model('activity', ActivitySchema) exports.Activity = Activity diff --git a/src/resources/activity/activityRouter.js b/src/resources/activity/activityRouter.js index 6136da10fd53bf472ca8404afca75d9029e022b4..4f3ceee56c2496bd9b9a9a8e7414b0b5a2a36bd9 100644 --- a/src/resources/activity/activityRouter.js +++ b/src/resources/activity/activityRouter.js @@ -1,19 +1,20 @@ const { Router } = require('express') const controllers = require('./activityControllers') +const { isLoggedIn, isMentor } = require('../../middlewares/auth') const router = Router() // /api/item router .route('/') - .get(controllers.default.getMany) - .post(controllers.default.createOne) + .get(isLoggedIn, controllers.default.getMany) + .post(isLoggedIn, isMentor, controllers.default.createOne) // /api/item/:id router .route('/:id') - .get(controllers.default.getOne) - .put(controllers.default.updateOne) - .delete(controllers.default.removeOne) + .get(isLoggedIn, controllers.default.getOne) + .put(isLoggedIn, isMentor, controllers.default.updateOne) + .delete(isLoggedIn, isMentor, controllers.default.removeOne) exports.default = router diff --git a/src/resources/attendance/attendanceControllers.js b/src/resources/attendance/attendanceControllers.js index d2d9c94d1fa10c773d605f61c99ce81b81459a70..1db3ae4a8a14bec3e792f870bd6bf2367266ce6e 100644 --- a/src/resources/attendance/attendanceControllers.js +++ b/src/resources/attendance/attendanceControllers.js @@ -6,6 +6,8 @@ const pickedKeys = ['_id', 'activity', 'user', 'state', 'comment'] exports.default = crudControllers(Attendance, pickedKeys) +// On create update activity + exports.default.getOne = async (req, res) => { try { const attendance = await Attendance.findOne({ _id: req.params.id }) diff --git a/src/resources/comment/commentControllers.js b/src/resources/comment/commentControllers.js index aec82b07861967c7a5a7d9a1fbff3178ddadf0aa..1e3c2bb6900ddd71ef0a661a87aa51b0e5f1ce53 100644 --- a/src/resources/comment/commentControllers.js +++ b/src/resources/comment/commentControllers.js @@ -1,4 +1,119 @@ const { crudControllers } = require('../../utils/crud') const { Comment } = require('./commentModel') +const { omit, pick } = require('lodash') -exports.default = crudControllers(Comment) +exports.default = crudControllers(Comment, ['creator', 'text', 'date']) + +function invalidIdResponse(res) { + return res + .status(403) + .json({ messages: ['You cannot modify other users comment.'] }) + .end() +} + +exports.default.getOne = async (req, res) => { + try { + const comment = await Comment.findOne({ + _id: req.params.id, + }) + .populate('creator', ['fullName', 'nickName']) + .lean() + .exec() + + if (comment.creator._id !== req.user._id && req.user.role !== 'mentor') { + return invalidIdResponse(res) + } + + return res + .status(200) + .json({ data: omit(comment, ['_id', '__v', 'creator._id']) }) + .end() + } catch (err) { + res.json(err) + } +} + +exports.default.getMany = async (req, res) => { + try { + const comments = await Comment.find() + .populate('_creator', '-_id fullName nickName') + .select(['-__v']) + .lean() + .exec() + + return res.status(200).json({ data: comments }).end() + } catch (err) { + console.log(err) + res.json(err) + } +} + +exports.default.createOne = async (req, res) => { + try { + var comment = await Comment.create({ + creator: req.user.schacc, + text: req.body.text, + }).catch((err) => console.log(err)) + + const retComment = await Comment.findById(comment._id) + .populate('_creator', '-_id fullName nickName schacc') + .lean() + .exec() + + retComment.creator = retComment._creator + + return res + .status(200) + .json({ + data: pick(retComment, ['_id', 'creator', 'text', 'createdAt']), + }) + .end() + } catch (err) { + console.log(err) + } +} + +exports.default.updateOne = async (req, res) => { + try { + var comment = await Comment.findById(req.params.id).populate('creator') + + if (comment.creator._id !== req.user._id) { + return invalidIdResponse(res) + } + + var updatedComment = await Comment.findOneAndUpdate( + { _id: req.params.id }, + { text: req.body.text }, + { new: true } + ) + .lean() + .exec() + + return res.status(200).json({ data: updatedComment }).end() + } catch (err) { + console.log(err) + } +} + +// TODO on delete remove from other models +exports.default.removeOne = async (req, res) => { + try { + const comment = await Comment.findById({ _id: req.params.id }) + .populate('creator') + .lean() + .exec() + + if (comment.creator._id !== req.user._id) { + return invalidIdResponse(res) + } + + const removed = await Comment.findByIdAndRemove({ _id: req.params.id }) + if (!removed) { + return res.status(404).end() + } + + return res.status(200).json({ data: comment }).end() + } catch (err) { + console.log(err) + } +} diff --git a/src/resources/comment/commentModel.js b/src/resources/comment/commentModel.js index de782a9ad446e26e80493f5e21e8575fd220b51d..dd3ba0dbd31de531a38b70bf4eebada5e48353d6 100644 --- a/src/resources/comment/commentModel.js +++ b/src/resources/comment/commentModel.js @@ -9,9 +9,10 @@ const CommentSchema = new mongoose.Schema( type: String, required: true, }, - date: { - type: Date, + isAnonim: { + type: Boolean, required: true, + default: false, }, }, { timestamps: true } diff --git a/src/resources/comment/commentRouter.js b/src/resources/comment/commentRouter.js index f5cb84a37c3648fb59b7d895200ca90c2e6b5eb8..d3e526cb47b55a9deca692514cd827a7e4830327 100644 --- a/src/resources/comment/commentRouter.js +++ b/src/resources/comment/commentRouter.js @@ -1,19 +1,20 @@ const { Router } = require('express') const controllers = require('./commentControllers') +const { isLoggedIn, isMentor } = require('../../middlewares/auth') const router = Router() // /api/item router .route('/') - .get(controllers.default.getMany) - .post(controllers.default.createOne) + .get(isLoggedIn, isMentor, controllers.default.getMany) + .post(isLoggedIn, controllers.default.createOne) // /api/item/:id router .route('/:id') - .get(controllers.default.getOne) - .put(controllers.default.updateOne) - .delete(controllers.default.removeOne) + .get(isLoggedIn, controllers.default.getOne) + .put(isLoggedIn, controllers.default.updateOne) + .delete(isLoggedIn, controllers.default.removeOne) exports.default = router diff --git a/src/resources/solution/solutionControllers.js b/src/resources/solution/solutionControllers.js index 524b0ebe157656e73a17df5322b43b8591d66058..19950f543b86e26ee71c863cf3ca3ca6b7373d2b 100644 --- a/src/resources/solution/solutionControllers.js +++ b/src/resources/solution/solutionControllers.js @@ -1,4 +1,188 @@ const { crudControllers } = require('../../utils/crud') const { Solution } = require('./solutionModel') +const { Task } = require('../task/taskModel') +const { pick } = require('lodash') exports.default = crudControllers(Solution) + +exports.default.createOne = async (req, res) => { + try { + const task = await Task.findById(req.body.task) + let solution + if (req.user.role === 'mentor' && req.body.creator) { + // Mentor Creates/Updates a Solution to someone + solution = await Solution.findOneAndUpdate( + { creator: req.body.creator, task: req.body.task }, + { ...req.body }, + { + upsert: true, // Create new One if there is no match + returnOriginal: false, + setDefaultsOnInsert: true, + runValidators: true, + } + ) + .lean() + .exec() + } else { + // Someone creates a solution + if (req.user.role != 'mentor') { + delete req.body.isAccepted + delete req.body.comment + } + if (task.deadline < new Date()) + return res.status(400).json({ + message: `Can't create new Solution! End date was ${task.deadline.toDateString()}`, + }) + + solution = await Solution.findOneAndUpdate( + { creator: req.user.schacc, task: req.body.task }, + { ...req.body }, + { + upsert: true, // Create new One if there is no match + returnOriginal: false, + setDefaultsOnInsert: true, + runValidators: true, + } + ) + .lean() + .exec() + } + if (task.solutions.indexOf(solution._id) == -1) { + task.solutions.push(solution._id) + task.save() + } + + let retSolution = await Solution.findById({ _id: solution._id }) + .populate('_creator', '-_id fullName nickName schacc') + .populate('comment', 'text creator createdAt') + .lean() + .exec() + + retSolution.creator = retSolution._creator + return res.status(200).json({ + data: pick(retSolution, [ + '_id', + 'creator', + 'title', + 'description', + 'file', + 'task', + 'comment', + 'createdAt', + 'updatedAt', + 'isAccepted', + ]), + }) + } catch (err) { + if (err.name == 'ValidationError') { + // Throwed by Mongoose + let messages = [] + for (field in err.errors) { + messages.push(err.errors[field].message) + } + return res.status(422).json({ messages }) + } + console.error(err) + return res.status(500).json({ message: err.message }) + } +} + +exports.default.getMany = async (req, res) => { + try { + let solutions = undefined + + if (req.user.role === 'mentor') + solutions = await Solution.find() + .populate('_creator', '-_id fullName nickName schacc') + .populate('comment', 'text creator createdAt') + .select('-__v') + .lean() + .exec() + else + solutions = await Solution.find({ user: req.user._id }) + .populate('_creator', '-_id fullName nickName') + .populate('comment', '-_id text creator createdAt') + .select('-__v') + .lean() + .exec() + + return res.status(200).json({ + data: solutions.map((e) => { + e.creator = e._creator + return pick(e, [ + '_id', + 'creator', + 'title', + 'description', + 'file', + 'task', + 'comment', + 'createdAt', + 'updatedAt', + 'isAccepted', + ]) + }), + }) + } catch (err) { + if (err.name == 'ValidationError') { + // Throwed by Mongoose + let messages = [] + for (field in err.errors) { + messages.push(err.errors[field].message) + } + return res.status(422).json({ messages }) + } + console.error(err) + return res.status(500).json({ message: err.message }) + } +} + +exports.default.getOne = async (req, res) => { + try { + let solution = await Solution.findById({ _id: req.params.id }) + .populate('_creator', '-_id fullName nickName') + .populate('comment', 'text creator createdAt') + .select('-__v') + .lean() + .exec() + + if (!solution) + return res.status(404).json({ messages: ['No such solution.'] }) + + if (req.user.schacc !== solution.creator && req.user.role !== 'mentor') { + return res + .status(403) + .json({ message: `You don't have permission for this solution.` }) + } + + return res.status(200).json({ + data: pick(solution, [ + '_id', + 'creator', + 'title', + 'description', + 'file', + 'task', + 'comment', + 'createdAt', + 'updatedAt', + 'isAccepted', + ]), + }) + } catch (err) { + if (err.name == 'ValidationError') { + // Throwed by Mongoose + let messages = [] + for (field in err.errors) { + messages.push(err.errors[field].message) + } + return res.status(422).json({ messages }) + } + console.error(err) + return res.status(500).json({ message: err.message }) + } +} + +exports.default.notSupported = async (req, res) => { + res.status(404).json({ data: { message: 'Not supported operation!' } }) +} diff --git a/src/resources/solution/solutionModel.js b/src/resources/solution/solutionModel.js index cc973a356d3aa438e5070efe4ffd8aafe4b181c5..e54cdcfbc6a7651d0981d40448d3fa7f87684c69 100644 --- a/src/resources/solution/solutionModel.js +++ b/src/resources/solution/solutionModel.js @@ -2,6 +2,11 @@ const mongoose = require('mongoose') const SolutionSchema = new mongoose.Schema( { + task: { + type: mongoose.Schema.Types.ObjectId, + ref: 'task', + required: true, + }, title: { type: String, required: true, @@ -14,9 +19,8 @@ const SolutionSchema = new mongoose.Schema( type: String, required: true, }, - user: { - type: mongoose.Schema.Types.ObjectId, - ref: 'user', + creator: { + type: String, required: true, }, comment: [ @@ -29,13 +33,18 @@ const SolutionSchema = new mongoose.Schema( isAccepted: { type: Boolean, required: true, + default: false, }, }, { timestamps: true } ) -// Careful with the docs, there are some deprecated ones -// https://mongoosejs.com/docs/guide.html +SolutionSchema.virtual('_creator', { + ref: 'user', + localField: 'creator', + foreignField: 'schacc', + justOne: true, +}) const Solution = mongoose.model('solution', SolutionSchema) diff --git a/src/resources/solution/solutionRouter.js b/src/resources/solution/solutionRouter.js index f123b0dd11e2ebeecdc6e434d2db4395cbca999c..7ec1a2f7723ad24da5e01153400d4018d14c8789 100644 --- a/src/resources/solution/solutionRouter.js +++ b/src/resources/solution/solutionRouter.js @@ -1,19 +1,20 @@ const { Router } = require('express') const controllers = require('./solutionControllers') +const { isLoggedIn, isAcceptedOrMentor } = require('../../middlewares/auth') const router = Router() // /api/item router .route('/') - .get(controllers.default.getMany) - .post(controllers.default.createOne) + .get(isLoggedIn, isAcceptedOrMentor, controllers.default.getMany) + .post(isLoggedIn, isAcceptedOrMentor, controllers.default.createOne) // /api/item/:id router .route('/:id') - .get(controllers.default.getOne) - .put(controllers.default.updateOne) - .delete(controllers.default.removeOne) + .get(isLoggedIn, isAcceptedOrMentor, controllers.default.getOne) + .delete(isLoggedIn, isAcceptedOrMentor, controllers.default.notSupported) + .put(isLoggedIn, isAcceptedOrMentor, controllers.default.notSupported) exports.default = router diff --git a/src/resources/task/taskControllers.js b/src/resources/task/taskControllers.js index 129937434f8712460cd87c931eda5eb92373d386..300f7bcd0156d92c79f51acde5a75b2f244ebd60 100644 --- a/src/resources/task/taskControllers.js +++ b/src/resources/task/taskControllers.js @@ -32,6 +32,7 @@ exports.default.getOne = async (req, res) => { 'createData', 'bit', 'creator', + 'solutions', ]), }) } else { @@ -69,6 +70,7 @@ exports.default.getMany = async (req, res) => { 'createData', 'bit', 'creator', + 'solutions', ]) }), }) @@ -87,6 +89,7 @@ exports.default.createOne = async (req, res) => { 'createData', 'bit', 'creator', + 'solutions', ]) ) res.status(201).json({ @@ -98,6 +101,7 @@ exports.default.createOne = async (req, res) => { 'createData', 'bit', 'creator', + 'solutions', ]), }) } catch (err) { diff --git a/src/resources/task/taskModel.js b/src/resources/task/taskModel.js index 1832ca9012baa87b6c0f31eaa607aa641d89f7b2..e7fe37c0efb8746867d0cda88443585079c8e99d 100644 --- a/src/resources/task/taskModel.js +++ b/src/resources/task/taskModel.js @@ -25,6 +25,13 @@ const TaskSchema = new mongoose.Schema( creator: { type: String, }, + solutions: [ + { + type: mongoose.Schema.Types.ObjectId, + ref: 'solution', + required: true, + }, + ], }, { timestamps: true } ) diff --git a/src/server.js b/src/server.js index c315bc67bffecfb26edc4e2d534ae62da2e83787..65284691938e82c04024b0a7ae12dcc353305e0e 100644 --- a/src/server.js +++ b/src/server.js @@ -44,7 +44,7 @@ app.use( app.use(passport.initialize()) app.use(passport.session()) -if (!config.default.isTest) { +if (!config.default.isTest || config.default.isDev) { require('./utils/oauthSetup') app.get( '/api/v1/login/authsch',