diff --git a/src/api/middlewares/is-auth.js b/src/api/middlewares/is-auth.js index 7c32a62e..6e5e8e5a 100644 --- a/src/api/middlewares/is-auth.js +++ b/src/api/middlewares/is-auth.js @@ -1,15 +1,27 @@ //require("dotenv").config(); const jwt = require("jsonwebtoken"); const Professor = require("../../models/professor"); +const Student = require('../../models/student'); +const log = require('debug')('is-auth'); +const logVerifyToken = log.extend('verifyToken'); +const logVerifyStudent = log.extend('verifyStudent'); exports.verifyToken=(req,res,next)=>{ - - let query = req.header('authorization').split(' '); - - let token = query[1]; //Authenticate - + //The exception thrown by the split method is handled sending a meessage to client + let token; + try { + let query = req.header('authorization').split(' '); + token = query[1]; //Authenticate + }catch(err) { + logVerifyToken(err); + return res.status(503).json({ + ok: false, + err + }); + } jwt.verify(token,process.env.JWT_SECRET,(error,decoded)=>{ //decoded is the payload + logVerifyToken(error); if(error){ return res.status(500).json({ ok:false, @@ -37,4 +49,24 @@ exports.verifyProfesssor = async (req,res,next) => { error:'El usuario no es profesor' }); } +} + +exports.verifyStudent = async (req, res, next) => { + let id=req.id; + try { + const student= await Student.findOne({ + where:{ + userId:id + } + }); + logVerifyStudent(`Student row: ${student}`) + next(); + } catch(error){ + logVerifyStudent(`Error: ${error}`) + return res.status(500).json({ + ok:false, + message: 'User is not a student', + error + }); + } } \ No newline at end of file diff --git a/src/api/routes/enrollment.js b/src/api/routes/enrollment.js new file mode 100644 index 00000000..8278f1ba --- /dev/null +++ b/src/api/routes/enrollment.js @@ -0,0 +1,9 @@ +const { Router } = require('express'); +const auth = require('../middlewares/is-auth'); +const EnrollmentController = require("../../controllers/enrollmentController"); + +const router = Router(); + +router.get('/', [auth.verifyToken, auth.verifyStudent],EnrollmentController.enrollStudentInACourse); + +module.exports = router; \ No newline at end of file diff --git a/src/api/routes/index.js b/src/api/routes/index.js index 56374bd9..a54b2982 100644 --- a/src/api/routes/index.js +++ b/src/api/routes/index.js @@ -14,6 +14,7 @@ module.exports = function(app) { { path: "/answer", file: "./answers" }, { path: "/question", file: "./questions" }, { path: "/exam", file: "./exams" }, + { path: "/enrollment", file: './enrollment'} ]; routes.forEach(route => { diff --git a/src/controllers/enrollmentController.js b/src/controllers/enrollmentController.js new file mode 100644 index 00000000..b5d9ffdf --- /dev/null +++ b/src/controllers/enrollmentController.js @@ -0,0 +1,35 @@ +const LOG = require("debug")("error:data"); +const Enrollment = require('../models/enrollment'); + +class EnrollmentController { + + + static async enrollStudentInACourse(req, res, next) { + LOG(`StudentId: ${req.query.studentId}`); + LOG(`courseId: ${req.query.courseId}`); + const studentId = req.query.studentId; + const courseId = req.query.courseId; + + try { + const enrollment = await Enrollment.create({ + studentId, + courseId, + calification: 85 + }); + return res.status(201).json({ + ok: true, + message: `You have enrolled to course with id ${courseId}`, + }); + }catch(err) { + e(err); + return res.status(400).json({ + ok: false, + message: `Something wrong happen enrolling in a course`, + err + }); + } + + } +} + +module.exports = EnrollmentController; \ No newline at end of file diff --git a/src/models/enrollment.js b/src/models/enrollment.js index cc2af5c5..ddfb270b 100644 --- a/src/models/enrollment.js +++ b/src/models/enrollment.js @@ -1,10 +1,18 @@ const Sequelize = require("sequelize"); const {sequelize} = require("../../config/db/mysql"); -const { Student } = require("./student"); -const Course = require("./course"); const Enrollment = sequelize.define('enrollments', -{ +{ + studentId: { + type: Sequelize.INTEGER, + allowNull: false, + references: { model: 'students', key: 'id' } + }, + courseId: { + type: Sequelize.INTEGER, + allowNull: false, + references: { model: 'courses', key: 'id' } + }, calification: { type: Sequelize.DOUBLE, allowNull: false @@ -13,9 +21,7 @@ const Enrollment = sequelize.define('enrollments', { timestamps: false } ); - -Enrollment.belongsTo(Course); -Enrollment.belongsTo(Student); +Enrollment.removeAttribute('id');//Remove the default field named 'id' module.exports = Enrollment diff --git a/src/models/student.js b/src/models/student.js index fae70463..0b612abc 100644 --- a/src/models/student.js +++ b/src/models/student.js @@ -1,6 +1,5 @@ const Sequelize = require("sequelize"); const { sequelize } = require("../../config/db/mysql"); -const Course = require("./course"); const Student = sequelize.define("students", { id: { @@ -9,6 +8,19 @@ const Student = sequelize.define("students", { primaryKey: true, autoIncrement:true }, + userId: { + type: Sequelize.INTEGER, + allowNull: false, + references: { model: 'users', key: 'id' } + }, + createdAt: { + allowNull: false, + type: Sequelize.DATE + }, + updatedAt: { + allowNull: false, + type: Sequelize.DATE + }, deletedAt: { type: Sequelize.DATE, allowNull: true, @@ -17,7 +29,4 @@ const Student = sequelize.define("students", { paranoid:true }); - -Student.hasMany(Course); - module.exports = Student; diff --git a/src/services/studentService.js b/src/services/studentService.js index e92db90a..1b3e577b 100644 --- a/src/services/studentService.js +++ b/src/services/studentService.js @@ -22,7 +22,7 @@ class StudentService { } catch (err) { e(err); - return new Error('An error has ocurred'); + return new Error(err); } } diff --git a/tests/rest/enrollment.js b/tests/rest/enrollment.js new file mode 100644 index 00000000..a7667c4b --- /dev/null +++ b/tests/rest/enrollment.js @@ -0,0 +1,116 @@ +const chai = require("chai"); +const chaiHttp = require("chai-http"); +const { expect } = require("chai"); + +const app = require("../../src/index"); +const { generateValidToken } = require("../../src/services/generateToken"); +const StudentService = require('../../src/services/studentService'); +const User = require("../../src/models/user"); +const Student = require('../../src/models/student'); +const Enrollment = require('../../src/models/enrollment'); +const { sequelize } = require("../../config/db/mysql"); +const log = require('debug')('enrollmenTest'); +const logCleanTables = log.extend('cleanTables'); +const server = require('../../src/index'); + + +//1. Para que un estudiante pueda enrolarse al curso este debe existir y haber iniciado sesión +//2. Un estudiante no puede enrolarse al mismo curso más de 1 vez +//3. Un estudiante tendrá accesos al curso cuando el profesor lo haya autorizado +//4. Un estudiante NO puede desenrolarse de un curso +//5. Un profesor puede desenrolar de un curso a un estudiante +//6. Solo los estudiantes pueden visualizar los cursos? + +chai.use(chaiHttp); +describe('Enrollment Tests', () => { + describe('A student can enroll to a course', () => { + const existentCourseId = 5; + let student, token; + before(async function(){ + student = await StudentService.createStudent({firstname: 'Rigoberto', lastname: 'Pérez', email: 'ed2@gmail.com', password: 'cisco123'}); + token = generateValidToken(student); + }); + + it.only('should return true if the course exists', (done) => { + chai.request(app) + .get("/api/v1/enrollment") + .query({studentId: student.id, courseId: existentCourseId}) + .set( 'Authorization', `Bearer ${token}` ) + .end(function(err, res) { + if (err) done(err); + expect(res).to.have.property('ok', true); + expect(res).to.have.status(201); + + done(); + }); + }); + + after(function(){ + const starterPromise = Promise.resolve(); + const asyncThingsToDo = [ + 'DELETE_ENROLLMENT_ROWS', + 'DELETE_STUDENT_ROWS', + 'DELETE_USER_ROWS', + 'ALTER_AUTOINCREMENT_STUDENT', + 'ALTER_AUTOINCREMENT_USER', + 'CLOSE_DB_CONNECTION', + 'CLOSE_SERVER_CONNECTION' + ]; + + + asyncThingsToDo.reduce(async (previous, task) => { + try { + const log = await previous; + logCleanTables(log); + } catch(err) { + logCleanTables(err) + } + return cleanTables(task); + },starterPromise); + + }); + + function cleanTables(task) { + switch(task) { + case 'DELETE_ENROLLMENT_ROWS': + logCleanTables('Inside DELETE_ENROLLMENT_ROWS'); + return Enrollment.destroy({ + where: { + studentId: student.id, + courseId: existentCourseId + }, + force: true + }).then(num => `Enrollment rows deleted ${num}`); + case 'DELETE_STUDENT_ROWS': + logCleanTables('Inside DELETE_STUDENT_ROWS'); + return Student.destroy({ + where: { + id: student.id + }, + force: true + }).then(num => `Student rows deleted ${num}`); + case 'DELETE_USER_ROWS': + logCleanTables('Inside DELETE_USER_ROWS'); + return User.destroy({ + where: { + id: student.userId + }, + force: true + }).then(num => `User rows deleted ${num}`); + case 'ALTER_AUTOINCREMENT_STUDENT': + logCleanTables('Inside ALTER_AUTOINCREMENT_STUDENT'); + return sequelize.query(`ALTER TABLE students AUTO_INCREMENT ${student.id}`) + //.then(result => result); + case 'ALTER_AUTOINCREMENT_USER': + logCleanTables('Inside ALTER_AUTOINCREMENT_USER'); + return sequelize.query(`ALTER TABLE users AUTO_INCREMENT ${student.userId}`) + //.then(result => result); + case 'CLOSE_DB_CONNECTION': + return sequelize.close(); + case 'CLOSE_SERVER_CONNECTION': + return server.close(); + } + } + }); +}); +