Source

API/src/controllers/question.controllers.js

/**
 * @fileoverview Question controller
 * 
 * @category Backend API
 * @subcategory Controllers
 * 
 * @module Question Controller
 * 
 * @requires ../models/course.models
 * @requires ../utils/errors
 * 
 * @description This module is responsible for handling all question related requests <br>
 * 
 * The following routes are handled by this module:: <br>
 * 
 * </br>
 * 
 * <b>POST</b> /question/new <i> - Add a new question to a particular exercise </i> <br>
 * <b>GET</b> /question/:id <i> - Get a particular question </i> <br>
 * <b>PATCH</b> /question/update/:id <i> - Update a particular question </i> <br>
 * <b>DELETE</b> /question/delete/:id <i> - Delete a particular question </i> <br>
 */

const { Question, Exercise, Video, Course } = require('../models/course.models')
const { translateDoc } = require('../utils/crowdin')
const { BadRequestError, NotFoundError } = require('../utils/errors')


// Add a new question to a particular exercise - req.body.exercise_id = the id of the exercise you want to add a question \
/**
 * Create Question
 * 
 * @description Create a new question for a particular exercise
 * 
 * @param {string} question
 * @param {string} correct_answer
 * @param {Array} options
 * @param {string} exercise_id 
 * 
 * @returns {MongooseObject} savedQuestion
 * 
 * @throws {error} if an error occured 
 */
exports.createQuestion = async (req, res, next) => {
    const {
        exercise_id, question,
        correct_answer, options
    } = req.body

    let exercise = await Exercise.findById(exercise_id)

    if (!exercise) {
        return next(new NotFoundError("Exercise not found"))
    }

    if (!options.includes(correct_answer)) {
        return next(new BadRequestError('Correct answer not in options'))
    }

    const question_obj = await Question.create({
        exercise: exercise?._id,
        question,
        correct_option: correct_answer,
        options
    })

    console.log(question_obj)
    return res.status(200).send({
        success: true,
        data: {
            question: question_obj
        }
    })
}


/**
 * Get Questions
 * 
 * @description 
 * By default it gets all available questions, 
 * if req.body is provided it'll be used as query params
 * to make a more streamlined query result
 * 
 * @param {string} exercise_id - Course id
 * @param {string} _id - questions id
 * @param {string} correct_option - Correct option to question ['A', 'B', 'C' ...]
 * 
 * @returns {ArrayObject} Questions
 * 
 * @throws {error} if an error occured
 */
exports.getQuestions = async (req, res, next) => {
    // if any specific query was added
    if (req.body) {
        const questions = await Question.find(req.body)

        return res.status(200).json({
            success: true,
            data: {
                questions
            }
        });
    }

    const questions = await Question.find().sort({ _id: -1 })

    return res.status(200).json({
        success: true,
        data: {
            questions
        }
    });
}

/**
 * Get qustion data
 * 
 * @description Returns the question and it's options
 * 
 * @param {string} id - id of question
 * 
 * @returns {Object} question
 * 
 * @throws {BadRequestError} if missing required param in request
 * @throws {NotFoundError} if question not found
 */
exports.getQuestionData = async (req, res, next) => {
    const question_id = req.params.id

    if (!question_id || question_id == ':id') {
        return next(new BadRequestError('Missing param `id` in request params'))
    }

    const question = await Question.findById(question_id)
    if (!question) {
        return next(new NotFoundError('Question not found'))
    }

    return res.status(200).send({
        success: true,
        data: {
            question
        }
    })
}

// Update data for a particular question
/**
 * Update Question data
 * 
 * @param {string} id - Id of the question
 * 
 * @returns {MongooseObject} - question
 * 
 * @throws {NotFoundError} if question not found
 * //TODO: Add feat to handle updating options and correct option
 */
exports.updateQuestion = async (req, res, next) => {
    const question = await Question.findByIdAndUpdate(req.params.id, { $set: req.body }, { new: true });

    if (!question) {
        return next(new NotFoundError('Question not found'))
    }

    const updated_question = await translateDoc(question)

    return res.status(200).json({
        success: true,
        data: {
            message: "Question Updated",
            question: updated_question
        }
    });
}

// Delete a particular question
/**
 * Delete a particular questions
 * 
 * @param {string} id - Id of the question
 * 
 * @throws {error} if an error occured
 */
exports.deleteQuestion = async (req, res, next) => {
    const question_id = req.params.id

    if (!question_id || question_id == ':id') {
        return next(new BadRequestError('Missing param `id` in request params'))
    }

    const question = await Question.findByIdAndDelete(question_id)
    if (!question) {
        return next(new NotFoundError("Question not found"))
    }

    return res.status(200).send({
        success: true,
        data: {
            message: "Question has been deleted successfully",
            question
        }
    })
}