mirror of
https://github.com/ets-cfuhrman-pfe/EvalueTonSavoir.git
synced 2025-08-11 21:23:54 -04:00
experimental (unfinished)
This commit is contained in:
parent
265f9a2b76
commit
e710e1a6fe
14 changed files with 536 additions and 277 deletions
|
|
@ -1,64 +1,25 @@
|
|||
const request = require('supertest');
|
||||
const app = require('../app.js');
|
||||
// const app = require('../routers/images.js');
|
||||
const { response } = require('express');
|
||||
import { Image } from '../models/image';
|
||||
import { User } from '../models/user';
|
||||
|
||||
const BASE_URL = '/image'
|
||||
describe('Image', () => {
|
||||
let mockUser: User;
|
||||
|
||||
describe("POST /upload", () => {
|
||||
beforeEach(() => {
|
||||
mockUser = new User({
|
||||
email: 'test@example.com',
|
||||
hashedPassword: 'hashedPassword123'
|
||||
});
|
||||
});
|
||||
|
||||
describe("when the jwt is not sent", () => {
|
||||
it('should create an image with the correct properties', () => {
|
||||
const fileName = 'test.png';
|
||||
const fileContent = Buffer.from('test content');
|
||||
const mimeType = 'image/png';
|
||||
const image = new Image(fileName, fileContent, mimeType, mockUser);
|
||||
|
||||
test('should respond with 401 status code', async () => {
|
||||
const response = await request(app).post(BASE_URL + "/upload").send()
|
||||
expect(response.statusCode).toBe(401)
|
||||
})
|
||||
// respond message Accès refusé. Aucun jeton fourni.
|
||||
|
||||
})
|
||||
|
||||
describe("when sent bad jwt", () => {
|
||||
// respond with 401
|
||||
// respond message Accès refusé. Jeton invalide.
|
||||
|
||||
})
|
||||
|
||||
describe("when sent no variables", () => {
|
||||
// respond message Paramètre requis manquant.
|
||||
// respond code 400
|
||||
|
||||
})
|
||||
|
||||
describe("when sent not an image file", () => {
|
||||
// respond code 505
|
||||
})
|
||||
|
||||
describe("when sent image file", () => {
|
||||
// respond code 200
|
||||
// json content type
|
||||
// test("should reply with content type json", async () => {
|
||||
// const response = await request(app).post(BASE_URL+'/upload').send()
|
||||
// expect(response.headers['content-type']).toEqual(expect.stringContaining('json'))
|
||||
// })
|
||||
})
|
||||
|
||||
})
|
||||
|
||||
describe("GET /get", () => {
|
||||
|
||||
describe("when not give id", () => {
|
||||
|
||||
})
|
||||
|
||||
describe("when not good id", () => {
|
||||
|
||||
})
|
||||
|
||||
describe("when good id", () => {
|
||||
// respond code 200
|
||||
// image content type
|
||||
// response has something
|
||||
|
||||
})
|
||||
|
||||
})
|
||||
expect(image.file_name).toBe(fileName);
|
||||
expect(image.file_content).toBe(fileContent);
|
||||
expect(image.mime_type).toBe(mimeType);
|
||||
expect(image.owner).toBe(mockUser);
|
||||
});
|
||||
});
|
||||
|
|
|
|||
64
server/__tests__/image.test.ts.old
Normal file
64
server/__tests__/image.test.ts.old
Normal file
|
|
@ -0,0 +1,64 @@
|
|||
const request = require('supertest');
|
||||
const app = require('../app.js');
|
||||
// const app = require('../routers/images.js');
|
||||
const { response } = require('express');
|
||||
|
||||
const BASE_URL = '/image'
|
||||
|
||||
describe("POST /upload", () => {
|
||||
|
||||
describe("when the jwt is not sent", () => {
|
||||
|
||||
test('should respond with 401 status code', async () => {
|
||||
const response = await request(app).post(BASE_URL + "/upload").send()
|
||||
expect(response.statusCode).toBe(401)
|
||||
})
|
||||
// respond message Accès refusé. Aucun jeton fourni.
|
||||
|
||||
})
|
||||
|
||||
describe("when sent bad jwt", () => {
|
||||
// respond with 401
|
||||
// respond message Accès refusé. Jeton invalide.
|
||||
|
||||
})
|
||||
|
||||
describe("when sent no variables", () => {
|
||||
// respond message Paramètre requis manquant.
|
||||
// respond code 400
|
||||
|
||||
})
|
||||
|
||||
describe("when sent not an image file", () => {
|
||||
// respond code 505
|
||||
})
|
||||
|
||||
describe("when sent image file", () => {
|
||||
// respond code 200
|
||||
// json content type
|
||||
// test("should reply with content type json", async () => {
|
||||
// const response = await request(app).post(BASE_URL+'/upload').send()
|
||||
// expect(response.headers['content-type']).toEqual(expect.stringContaining('json'))
|
||||
// })
|
||||
})
|
||||
|
||||
})
|
||||
|
||||
describe("GET /get", () => {
|
||||
|
||||
describe("when not give id", () => {
|
||||
|
||||
})
|
||||
|
||||
describe("when not good id", () => {
|
||||
|
||||
})
|
||||
|
||||
describe("when good id", () => {
|
||||
// respond code 200
|
||||
// image content type
|
||||
// response has something
|
||||
|
||||
})
|
||||
|
||||
})
|
||||
|
|
@ -27,4 +27,4 @@ class DBConnection {
|
|||
}
|
||||
}
|
||||
|
||||
export default DBConnection;
|
||||
export default new DBConnection();
|
||||
|
|
|
|||
|
|
@ -1,133 +1,110 @@
|
|||
exports.UNAUTHORIZED_NO_TOKEN_GIVEN = {
|
||||
export const UNAUTHORIZED_NO_TOKEN_GIVEN = {
|
||||
message: 'Accès refusé. Aucun jeton fourni.',
|
||||
code: 401
|
||||
}
|
||||
exports.UNAUTHORIZED_INVALID_TOKEN = {
|
||||
};
|
||||
export const UNAUTHORIZED_INVALID_TOKEN = {
|
||||
message: 'Accès refusé. Jeton invalide.',
|
||||
code: 401
|
||||
}
|
||||
};
|
||||
|
||||
exports.MISSING_REQUIRED_PARAMETER = {
|
||||
export const MISSING_REQUIRED_PARAMETER = {
|
||||
message: 'Paramètre requis manquant.',
|
||||
code: 400
|
||||
}
|
||||
};
|
||||
|
||||
exports.USER_ALREADY_EXISTS = {
|
||||
export const USER_ALREADY_EXISTS = {
|
||||
message: 'L\'utilisateur existe déjà.',
|
||||
code: 400
|
||||
}
|
||||
exports.LOGIN_CREDENTIALS_ERROR = {
|
||||
};
|
||||
export const LOGIN_CREDENTIALS_ERROR = {
|
||||
message: 'L\'email et le mot de passe ne correspondent pas.',
|
||||
code: 400
|
||||
}
|
||||
exports.GENERATE_PASSWORD_ERROR = {
|
||||
};
|
||||
export const GENERATE_PASSWORD_ERROR = {
|
||||
message: 'Une erreur s\'est produite lors de la création d\'un nouveau mot de passe.',
|
||||
code: 400
|
||||
}
|
||||
exports.UPDATE_PASSWORD_ERROR = {
|
||||
};
|
||||
export const UPDATE_PASSWORD_ERROR = {
|
||||
message: 'Une erreur s\'est produite lors de la mise à jours du mot de passe.',
|
||||
code: 400
|
||||
}
|
||||
exports.DELETE_USER_ERROR = {
|
||||
message: 'Une erreur s\'est produite lors de supression de l\'utilisateur.',
|
||||
};
|
||||
export const DELETE_USER_ERROR = {
|
||||
message: 'Une erreur s\'est produite lors de suppression de l\'utilisateur.',
|
||||
code: 400
|
||||
}
|
||||
};
|
||||
|
||||
exports.IMAGE_NOT_FOUND = {
|
||||
export const IMAGE_NOT_FOUND = {
|
||||
message: 'Nous n\'avons pas trouvé l\'image.',
|
||||
code: 404
|
||||
}
|
||||
};
|
||||
|
||||
exports.QUIZ_NOT_FOUND = {
|
||||
export const QUIZ_NOT_FOUND = {
|
||||
message: 'Aucun quiz portant cet identifiant n\'a été trouvé.',
|
||||
code: 404
|
||||
}
|
||||
exports.QUIZ_ALREADY_EXISTS = {
|
||||
message: 'Le quiz existe déja.',
|
||||
};
|
||||
export const QUIZ_ALREADY_EXISTS = {
|
||||
message: 'Le quiz existe déjà.',
|
||||
code: 400
|
||||
}
|
||||
exports.UPDATE_QUIZ_ERROR = {
|
||||
};
|
||||
export const UPDATE_QUIZ_ERROR = {
|
||||
message: 'Une erreur s\'est produite lors de la mise à jours du quiz.',
|
||||
code: 400
|
||||
}
|
||||
exports.DELETE_QUIZ_ERROR = {
|
||||
message: 'Une erreur s\'est produite lors de la supression du quiz.',
|
||||
};
|
||||
export const DELETE_QUIZ_ERROR = {
|
||||
message: 'Une erreur s\'est produite lors de la suppression du quiz.',
|
||||
code: 400
|
||||
}
|
||||
exports.GETTING_QUIZ_ERROR = {
|
||||
};
|
||||
export const GETTING_QUIZ_ERROR = {
|
||||
message: 'Une erreur s\'est produite lors de la récupération du quiz.',
|
||||
code: 400
|
||||
}
|
||||
exports.MOVING_QUIZ_ERROR = {
|
||||
};
|
||||
export const MOVING_QUIZ_ERROR = {
|
||||
message: 'Une erreur s\'est produite lors du déplacement du quiz.',
|
||||
code: 400
|
||||
}
|
||||
exports.DUPLICATE_QUIZ_ERROR = {
|
||||
};
|
||||
export const DUPLICATE_QUIZ_ERROR = {
|
||||
message: 'Une erreur s\'est produite lors de la duplication du quiz.',
|
||||
code: 400
|
||||
}
|
||||
exports.COPY_QUIZ_ERROR = {
|
||||
};
|
||||
export const COPY_QUIZ_ERROR = {
|
||||
message: 'Une erreur s\'est produite lors de la copie du quiz.',
|
||||
code: 400
|
||||
}
|
||||
};
|
||||
|
||||
exports.FOLDER_NOT_FOUND = {
|
||||
export const FOLDER_NOT_FOUND = {
|
||||
message: 'Aucun dossier portant cet identifiant n\'a été trouvé.',
|
||||
code: 404
|
||||
}
|
||||
exports.FOLDER_ALREADY_EXISTS = {
|
||||
message: 'Le dossier existe déja.',
|
||||
};
|
||||
export const FOLDER_ALREADY_EXISTS = {
|
||||
message: 'Le dossier existe déjà.',
|
||||
code: 400
|
||||
}
|
||||
exports.UPDATE_FOLDER_ERROR = {
|
||||
};
|
||||
export const UPDATE_FOLDER_ERROR = {
|
||||
message: 'Une erreur s\'est produite lors de la mise à jours du dossier.',
|
||||
code: 400
|
||||
}
|
||||
exports.DELETE_FOLDER_ERROR = {
|
||||
message: 'Une erreur s\'est produite lors de la supression du dossier.',
|
||||
};
|
||||
export const DELETE_FOLDER_ERROR = {
|
||||
message: 'Une erreur s\'est produite lors de la suppression du dossier.',
|
||||
code: 400
|
||||
}
|
||||
exports.GETTING_FOLDER_ERROR = {
|
||||
};
|
||||
export const GETTING_FOLDER_ERROR = {
|
||||
message: 'Une erreur s\'est produite lors de la récupération du dossier.',
|
||||
code: 400
|
||||
}
|
||||
exports.MOVING_FOLDER_ERROR = {
|
||||
};
|
||||
export const MOVING_FOLDER_ERROR = {
|
||||
message: 'Une erreur s\'est produite lors du déplacement du dossier.',
|
||||
code: 400
|
||||
}
|
||||
exports.DUPLICATE_FOLDER_ERROR = {
|
||||
};
|
||||
export const DUPLICATE_FOLDER_ERROR = {
|
||||
message: 'Une erreur s\'est produite lors de la duplication du dossier.',
|
||||
code: 400
|
||||
}
|
||||
exports.COPY_FOLDER_ERROR = {
|
||||
};
|
||||
export const COPY_FOLDER_ERROR = {
|
||||
message: 'Une erreur s\'est produite lors de la copie du dossier.',
|
||||
code: 400
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
exports.NOT_IMPLEMENTED = {
|
||||
export const NOT_IMPLEMENTED = {
|
||||
message: 'Route not implemented yet!',
|
||||
code: 400
|
||||
}
|
||||
|
||||
|
||||
// static ok(res, results) {200
|
||||
// static badRequest(res, message) {400
|
||||
// static unauthorized(res, message) {401
|
||||
// static notFound(res, message) {404
|
||||
// static serverError(res, message) {505
|
||||
};
|
||||
|
|
|
|||
|
|
@ -1,13 +1,22 @@
|
|||
const model = require('../models/quiz.js');
|
||||
const folderModel = require('../models/folders.js');
|
||||
const emailer = require('../config/email.js');
|
||||
|
||||
const AppError = require('../middleware/AppError.js');
|
||||
const { MISSING_REQUIRED_PARAMETER, NOT_IMPLEMENTED, QUIZ_NOT_FOUND, FOLDER_NOT_FOUND, QUIZ_ALREADY_EXISTS, GETTING_QUIZ_ERROR, DELETE_QUIZ_ERROR, UPDATE_QUIZ_ERROR, MOVING_QUIZ_ERROR, DUPLICATE_QUIZ_ERROR, COPY_QUIZ_ERROR } = require('../constants/errorCodes');
|
||||
import { Request, Response, NextFunction } from 'express';
|
||||
import AppError from '../middleware/AppError';
|
||||
import { MISSING_REQUIRED_PARAMETER, NOT_IMPLEMENTED, QUIZ_NOT_FOUND, FOLDER_NOT_FOUND, QUIZ_ALREADY_EXISTS, GETTING_QUIZ_ERROR, DELETE_QUIZ_ERROR, UPDATE_QUIZ_ERROR, MOVING_QUIZ_ERROR } from '../constants/errorCodes';
|
||||
import { Quiz } from '../models/quiz';
|
||||
import { Folder } from '../models/folder';
|
||||
import Emailer from '../config/email';
|
||||
|
||||
class QuizController {
|
||||
private quiz: Quiz;
|
||||
private folder: Folder;
|
||||
private emailer: Emailer;
|
||||
|
||||
async create(req, res, next) {
|
||||
constructor() {
|
||||
this.folder = new Folder();
|
||||
this.quiz = new Quiz();
|
||||
this.emailer = new Emailer();
|
||||
}
|
||||
|
||||
async create(req: Request, res: Response, next: NextFunction) {
|
||||
try {
|
||||
const { title, content, folderId } = req.body;
|
||||
|
||||
|
|
@ -16,13 +25,13 @@ class QuizController {
|
|||
}
|
||||
|
||||
// Is this folder mine
|
||||
const owner = await folderModel.getOwner(folderId);
|
||||
const owner = await this.folder.getOwner(folderId);
|
||||
|
||||
if (owner != req.user.userId) {
|
||||
throw new AppError(FOLDER_NOT_FOUND);
|
||||
}
|
||||
|
||||
const result = await model.create(title, content, folderId, req.user.userId);
|
||||
const result = await this.quiz.create(title, content, folderId, req.user.userId);
|
||||
|
||||
if (!result) {
|
||||
throw new AppError(QUIZ_ALREADY_EXISTS);
|
||||
|
|
@ -38,7 +47,7 @@ class QuizController {
|
|||
}
|
||||
}
|
||||
|
||||
async get(req, res, next) {
|
||||
async get(req: Request, res: Response, next: NextFunction) {
|
||||
try {
|
||||
const { quizId } = req.params;
|
||||
|
||||
|
|
@ -47,7 +56,7 @@ class QuizController {
|
|||
}
|
||||
|
||||
|
||||
const content = await model.getContent(quizId);
|
||||
const content = await this.quiz.getContent(quizId);
|
||||
|
||||
if (!content) {
|
||||
throw new AppError(GETTING_QUIZ_ERROR);
|
||||
|
|
@ -68,7 +77,7 @@ class QuizController {
|
|||
}
|
||||
}
|
||||
|
||||
async delete(req, res, next) {
|
||||
async delete(req: Request, res: Response, next: NextFunction) {
|
||||
try {
|
||||
const { quizId } = req.params;
|
||||
|
||||
|
|
@ -77,13 +86,13 @@ class QuizController {
|
|||
}
|
||||
|
||||
// Is this quiz mine
|
||||
const owner = await model.getOwner(quizId);
|
||||
const owner = await this.quiz.getOwner(quizId);
|
||||
|
||||
if (owner != req.user.userId) {
|
||||
throw new AppError(QUIZ_NOT_FOUND);
|
||||
}
|
||||
|
||||
const result = await model.delete(quizId);
|
||||
const result = await this.quiz.delete(quizId);
|
||||
|
||||
if (!result) {
|
||||
throw new AppError(DELETE_QUIZ_ERROR);
|
||||
|
|
@ -99,7 +108,7 @@ class QuizController {
|
|||
}
|
||||
}
|
||||
|
||||
async update(req, res, next) {
|
||||
async update(req: Request, res: Response, next: NextFunction) {
|
||||
try {
|
||||
const { quizId, newTitle, newContent } = req.body;
|
||||
|
||||
|
|
@ -108,13 +117,13 @@ class QuizController {
|
|||
}
|
||||
|
||||
// Is this quiz mine
|
||||
const owner = await model.getOwner(quizId);
|
||||
const owner = await this.quiz.getOwner(quizId);
|
||||
|
||||
if (owner != req.user.userId) {
|
||||
throw new AppError(QUIZ_NOT_FOUND);
|
||||
}
|
||||
|
||||
const result = await model.update(quizId, newTitle, newContent);
|
||||
const result = await this.quiz.update(quizId, newTitle, newContent);
|
||||
|
||||
if (!result) {
|
||||
throw new AppError(UPDATE_QUIZ_ERROR);
|
||||
|
|
@ -130,7 +139,7 @@ class QuizController {
|
|||
}
|
||||
}
|
||||
|
||||
async move(req, res, next) {
|
||||
async move(req: Request, res: Response, next: NextFunction) {
|
||||
try {
|
||||
const { quizId, newFolderId } = req.body;
|
||||
|
||||
|
|
@ -139,20 +148,20 @@ class QuizController {
|
|||
}
|
||||
|
||||
// Is this quiz mine
|
||||
const quizOwner = await model.getOwner(quizId);
|
||||
const quizOwner = await this.quiz.getOwner(quizId);
|
||||
|
||||
if (quizOwner != req.user.userId) {
|
||||
throw new AppError(QUIZ_NOT_FOUND);
|
||||
}
|
||||
|
||||
// Is this folder mine
|
||||
const folderOwner = await folderModel.getOwner(newFolderId);
|
||||
const folderOwner = await this.folder.getOwner(newFolderId);
|
||||
|
||||
if (folderOwner != req.user.userId) {
|
||||
throw new AppError(FOLDER_NOT_FOUND);
|
||||
}
|
||||
|
||||
const result = await model.move(quizId, newFolderId);
|
||||
const result = await this.quiz.move(quizId, newFolderId);
|
||||
|
||||
if (!result) {
|
||||
throw new AppError(MOVING_QUIZ_ERROR);
|
||||
|
|
@ -169,8 +178,7 @@ class QuizController {
|
|||
|
||||
}
|
||||
|
||||
|
||||
async copy(req, res, next) {
|
||||
async copy(req: Request, res: Response, next: NextFunction) {
|
||||
const { quizId, newTitle, folderId } = req.body;
|
||||
|
||||
if (!quizId || !newTitle || !folderId) {
|
||||
|
|
@ -178,32 +186,9 @@ class QuizController {
|
|||
}
|
||||
|
||||
throw new AppError(NOT_IMPLEMENTED);
|
||||
// const { quizId } = req.params;
|
||||
// const { newUserId } = req.body;
|
||||
|
||||
// try {
|
||||
// //Trouver le quiz a dupliquer
|
||||
// const conn = db.getConnection();
|
||||
// const quiztoduplicate = await conn.collection('quiz').findOne({ _id: new ObjectId(quizId) });
|
||||
// if (!quiztoduplicate) {
|
||||
// throw new Error("Quiz non trouvé");
|
||||
// }
|
||||
// console.log(quiztoduplicate);
|
||||
// //Suppression du id du quiz pour ne pas le répliquer
|
||||
// delete quiztoduplicate._id;
|
||||
// //Ajout du duplicata
|
||||
// await conn.collection('quiz').insertOne({ ...quiztoduplicate, userId: new ObjectId(newUserId) });
|
||||
// res.json(Response.ok("Dossier dupliqué avec succès pour un autre utilisateur"));
|
||||
|
||||
// } catch (error) {
|
||||
// if (error.message.startsWith("Quiz non trouvé")) {
|
||||
// return res.status(404).json(Response.badRequest(error.message));
|
||||
// }
|
||||
// res.status(500).json(Response.serverError(error.message));
|
||||
// }
|
||||
}
|
||||
|
||||
async deleteQuizzesByFolderId(req, res, next) {
|
||||
async deleteQuizzesByFolderId(req: Request, res: Response, next: NextFunction) {
|
||||
try {
|
||||
const { folderId } = req.body;
|
||||
|
||||
|
|
@ -212,7 +197,7 @@ class QuizController {
|
|||
}
|
||||
|
||||
// Call the method from the Quiz model to delete quizzes by folder ID
|
||||
await Quiz.deleteQuizzesByFolderId(folderId);
|
||||
await this.quiz.deleteQuizzesByFolderId(folderId);
|
||||
|
||||
return res.status(200).json({
|
||||
message: 'Quizzes deleted successfully.'
|
||||
|
|
@ -222,37 +207,37 @@ class QuizController {
|
|||
}
|
||||
}
|
||||
|
||||
async duplicate(req, res, next) {
|
||||
async duplicate(req: Request, res: Response, next: NextFunction) {
|
||||
const { quizId } = req.body;
|
||||
|
||||
try {
|
||||
const newQuizId = await model.duplicate(quizId,req.user.userId);
|
||||
const newQuizId = await this.quiz.duplicate(quizId, req.user.userId);
|
||||
res.status(200).json({ success: true, newQuizId });
|
||||
} catch (error) {
|
||||
return next(error);
|
||||
}
|
||||
}
|
||||
|
||||
async quizExists(title, userId) {
|
||||
async quizExists(title: string, userId: string) {
|
||||
try {
|
||||
const existingFile = await model.quizExists(title, userId);
|
||||
const existingFile = await this.quiz.quizExists(title, userId);
|
||||
return existingFile !== null;
|
||||
} catch (error) {
|
||||
throw new AppError(GETTING_QUIZ_ERROR);
|
||||
}
|
||||
}
|
||||
|
||||
async Share(req, res, next) {
|
||||
async Share(req: Request, res: Response, next: NextFunction) {
|
||||
try {
|
||||
const { quizId, email } = req.body;
|
||||
|
||||
if ( !quizId || !email) {
|
||||
if (!quizId || !email) {
|
||||
throw new AppError(MISSING_REQUIRED_PARAMETER);
|
||||
}
|
||||
|
||||
const link = `${process.env.FRONTEND_URL}/teacher/Share/${quizId}`;
|
||||
|
||||
emailer.quizShare(email, link);
|
||||
this.emailer.quizShare(email, link);
|
||||
|
||||
return res.status(200).json({
|
||||
message: 'Quiz partagé avec succès.'
|
||||
|
|
@ -264,15 +249,15 @@ class QuizController {
|
|||
}
|
||||
}
|
||||
|
||||
async getShare(req, res, next) {
|
||||
async getShare(req: Request, res: Response, next: NextFunction) {
|
||||
try {
|
||||
const { quizId } = req.params;
|
||||
|
||||
if ( !quizId ) {
|
||||
if (!quizId) {
|
||||
throw new AppError(MISSING_REQUIRED_PARAMETER);
|
||||
}
|
||||
|
||||
const content = await model.getContent(quizId);
|
||||
const content = await this.quiz.getContent(quizId);
|
||||
|
||||
if (!content) {
|
||||
throw new AppError(GETTING_QUIZ_ERROR);
|
||||
|
|
@ -288,7 +273,7 @@ class QuizController {
|
|||
}
|
||||
}
|
||||
|
||||
async receiveShare(req, res, next) {
|
||||
async receiveShare(req: Request, res: Response, next: NextFunction) {
|
||||
try {
|
||||
const { quizId, folderId } = req.body;
|
||||
|
||||
|
|
@ -296,17 +281,17 @@ class QuizController {
|
|||
throw new AppError(MISSING_REQUIRED_PARAMETER);
|
||||
}
|
||||
|
||||
const folderOwner = await folderModel.getOwner(folderId);
|
||||
const folderOwner = await this.folder.getOwner(folderId);
|
||||
if (folderOwner != req.user.userId) {
|
||||
throw new AppError(FOLDER_NOT_FOUND);
|
||||
}
|
||||
|
||||
const content = await model.getContent(quizId);
|
||||
const content = await this.quiz.getContent(quizId);
|
||||
if (!content) {
|
||||
throw new AppError(GETTING_QUIZ_ERROR);
|
||||
}
|
||||
|
||||
const result = await model.create(content.title, content.content, folderId, req.user.userId);
|
||||
const result = await this.quiz.create(content.title, content.content, folderId, req.user.userId);
|
||||
if (!result) {
|
||||
throw new AppError(QUIZ_ALREADY_EXISTS);
|
||||
}
|
||||
|
|
@ -319,8 +304,6 @@ class QuizController {
|
|||
return next(error);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
module.exports = new QuizController;
|
||||
export default new QuizController();
|
||||
|
|
|
|||
23
server/docs/addFolder-RDCU.puml
Normal file
23
server/docs/addFolder-RDCU.puml
Normal file
|
|
@ -0,0 +1,23 @@
|
|||
@startuml addFolder-RDCU
|
||||
skinparam style strictuml
|
||||
skinparam SequenceMessageAlignment center
|
||||
|
||||
participant ":Folder\nController" as FC
|
||||
participant ":Évalue\nTon\nSavoir" as ETS
|
||||
participant ":Map<string, User>" as MUS
|
||||
participant "user:User" as U
|
||||
participant "f:Folder" as F
|
||||
|
||||
-> FC: addFolder(folderName: string,\nuserId: string)
|
||||
FC -> ETS : user = getUser(userId: string)
|
||||
note right: by Expert
|
||||
ETS -> MUS : user = find(userId)
|
||||
note right: id to object
|
||||
FC -> ETS : f = createFolder(folderName: string,\nuser: User)
|
||||
create F
|
||||
ETS -->> F : <<create>>
|
||||
note right: by Creator (ETS\naggregates Folder)
|
||||
ETS -> U : addFolder(f)
|
||||
note right: by Expert
|
||||
|
||||
@enduml
|
||||
38
server/docs/addFolder-serviceLayer.puml
Normal file
38
server/docs/addFolder-serviceLayer.puml
Normal file
|
|
@ -0,0 +1,38 @@
|
|||
@startuml
|
||||
skinparam style strictuml
|
||||
participant ":Folder\nController" as FC
|
||||
participant ":Évalue\nTon\nSavoir" as ETS
|
||||
participant ":Map<string, User>" as MUS
|
||||
participant "user:User" as U
|
||||
participant "f:Folder" as F
|
||||
participant "FolderService" as FS
|
||||
participant "UserRepository" as UR
|
||||
participant "FolderRepository" as FR
|
||||
database "Database" as DB
|
||||
|
||||
-> FC: addFolder(folderName: string,\nuserId: string)
|
||||
FC -> ETS : user = getUser(userId: string)
|
||||
note right: by Expert
|
||||
ETS -> MUS : user = find(userId)
|
||||
note right: id to object
|
||||
FC -> ETS : f = createFolder(folderName: string,\nuser: User)
|
||||
create F
|
||||
ETS -->> F : <<create>>
|
||||
note right: by Creator (ETS\naggregates Folder)
|
||||
ETS -> U : addFolder(f)
|
||||
note right: by Expert
|
||||
|
||||
ETS -> FS: addFolderToUser(user, f)
|
||||
FS -> FR: save(f)
|
||||
FR -> DB: insertOne(f)
|
||||
DB --> FR: insertedId
|
||||
FR --> FS: save result
|
||||
|
||||
FS -> UR: update(user)
|
||||
UR -> DB: updateOne({ _id: user._id }, { $set: { folders: user.folders } })
|
||||
DB --> UR: update result
|
||||
UR --> FS: update result
|
||||
|
||||
FS --> ETS: success
|
||||
ETS --> FC: success
|
||||
@enduml
|
||||
34
server/docs/mdd.puml
Normal file
34
server/docs/mdd.puml
Normal file
|
|
@ -0,0 +1,34 @@
|
|||
@startuml MDD EvalueTonSavoir
|
||||
skinparam style strictuml
|
||||
hide empty members
|
||||
|
||||
class "EvalueTonSavoir" as ETS
|
||||
|
||||
class User {
|
||||
id: string
|
||||
email: string
|
||||
hashedPassword: string
|
||||
createdAt: Date
|
||||
}
|
||||
|
||||
class Quiz {
|
||||
id: string
|
||||
title: string
|
||||
content: string
|
||||
createdAt: Date
|
||||
updatedAt: Date
|
||||
}
|
||||
|
||||
class Folder {
|
||||
id: string
|
||||
title: string
|
||||
createdAt: Date
|
||||
}
|
||||
|
||||
User "1" -- "1..*" Folder : Creates >
|
||||
Folder "1" *-- "1..*" Quiz : Contains >
|
||||
User "1" *-- "1..*" Quiz : Creates >
|
||||
ETS "1" *-- "1..*" User : Contains >
|
||||
ETS "1" *-- "1..*" Folder : Contains >
|
||||
ETS "1" *-- "1..*" Quiz : Contains >
|
||||
@enduml
|
||||
|
|
@ -1,8 +1,16 @@
|
|||
interface ErrorCode {
|
||||
message: string;
|
||||
code: number;
|
||||
}
|
||||
|
||||
class AppError extends Error {
|
||||
constructor (errorCode) {
|
||||
super(errorCode.message)
|
||||
statusCode: number;
|
||||
|
||||
constructor(errorCode: ErrorCode) {
|
||||
super(errorCode.message);
|
||||
this.statusCode = errorCode.code;
|
||||
Object.setPrototypeOf(this, AppError.prototype); // Ensure the prototype chain is correctly set
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = AppError;
|
||||
export default AppError;
|
||||
|
|
|
|||
|
|
@ -1,37 +1,35 @@
|
|||
const jwt = require('jsonwebtoken')
|
||||
const dotenv = require('dotenv')
|
||||
const AppError = require('./AppError.js');
|
||||
const { UNAUTHORIZED_NO_TOKEN_GIVEN, UNAUTHORIZED_INVALID_TOKEN } = require('../constants/errorCodes');
|
||||
import jwt from 'jsonwebtoken';
|
||||
import dotenv from 'dotenv';
|
||||
import AppError from './AppError';
|
||||
import { UNAUTHORIZED_NO_TOKEN_GIVEN, UNAUTHORIZED_INVALID_TOKEN } from '../constants/errorCodes';
|
||||
import { Request, Response, NextFunction } from 'express';
|
||||
|
||||
dotenv.config();
|
||||
|
||||
class Token {
|
||||
|
||||
create(email, userId) {
|
||||
return jwt.sign({ email, userId }, process.env.JWT_SECRET);
|
||||
create(email: string, userId: string): string {
|
||||
return jwt.sign({ email, userId }, process.env.JWT_SECRET as string, { expiresIn: '2h' });
|
||||
}
|
||||
|
||||
authenticate(req, res, next) {
|
||||
authenticate(req: Request, res: Response, next: NextFunction): void {
|
||||
try {
|
||||
const token = req.header('Authorization') && req.header('Authorization').split(' ')[1];
|
||||
const authHeader = req.header('Authorization');
|
||||
const token = authHeader && authHeader.split(' ')[1];
|
||||
if (!token) {
|
||||
throw new AppError(UNAUTHORIZED_NO_TOKEN_GIVEN);
|
||||
}
|
||||
|
||||
jwt.verify(token, process.env.JWT_SECRET, (error, payload) => {
|
||||
jwt.verify(token, process.env.JWT_SECRET as string, (error, payload) => {
|
||||
if (error) {
|
||||
throw new AppError(UNAUTHORIZED_INVALID_TOKEN)
|
||||
throw new AppError(UNAUTHORIZED_INVALID_TOKEN);
|
||||
}
|
||||
|
||||
req.user = payload;
|
||||
next();
|
||||
});
|
||||
|
||||
} catch (error) {
|
||||
return next(error);
|
||||
next(error);
|
||||
}
|
||||
|
||||
return next();
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = new Token();
|
||||
export default new Token();
|
||||
|
|
|
|||
|
|
@ -1,42 +1,12 @@
|
|||
import db from '../config/db';
|
||||
import { ObjectId } from 'mongodb';
|
||||
import { User } from "./user";
|
||||
|
||||
export class Image {
|
||||
async upload(file: Express.Multer.File, userId: string): Promise<string> {
|
||||
await db.connect();
|
||||
const conn = db.getConnection();
|
||||
|
||||
const imagesCollection = conn.collection('images');
|
||||
|
||||
const newImage = {
|
||||
userId: userId,
|
||||
file_name: file.originalname,
|
||||
file_content: file.buffer.toString('base64'),
|
||||
mime_type: file.mimetype,
|
||||
created_at: new Date(),
|
||||
};
|
||||
|
||||
const result = await imagesCollection.insertOne(newImage);
|
||||
|
||||
return result.insertedId.toString();
|
||||
constructor(public file_name: string, public file_content: Buffer, public mime_type: string, public owner: User) {
|
||||
this.file_name = file_name;
|
||||
this.file_content = file_content;
|
||||
this.mime_type = mime_type;
|
||||
this.owner = owner;
|
||||
}
|
||||
|
||||
async get(id: string): Promise<{ file_name: string; file_content: Buffer; mime_type: string } | null> {
|
||||
await db.connect();
|
||||
const conn = db.getConnection();
|
||||
|
||||
const imagesCollection = conn.collection('images');
|
||||
|
||||
const result = await imagesCollection.findOne({ _id: new ObjectId(id) });
|
||||
|
||||
if (!result) return null;
|
||||
|
||||
return {
|
||||
file_name: result.file_name,
|
||||
file_content: Buffer.from(result.file_content, 'base64'),
|
||||
mime_type: result.mime_type,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
export default new Image();
|
||||
|
|
|
|||
139
server/repositories/folderRepository.ts
Normal file
139
server/repositories/folderRepository.ts
Normal file
|
|
@ -0,0 +1,139 @@
|
|||
import { Db, ObjectId } from 'mongodb';
|
||||
import DBConnection from '../config/db';
|
||||
import { Folder } from '../models/folder';
|
||||
|
||||
class FolderRepository {
|
||||
private db: DBConnection;
|
||||
|
||||
constructor() {
|
||||
this.db = new DBConnection();
|
||||
}
|
||||
|
||||
async createFolder(folder: Folder): Promise<ObjectId | null> {
|
||||
await this.db.connect();
|
||||
const conn: Db = this.db.getConnection();
|
||||
|
||||
const folderCollection = conn.collection('folders');
|
||||
|
||||
const existingFolder = await folderCollection.findOne({ title: folder.title, userId: folder.userId });
|
||||
|
||||
if (existingFolder) throw new Error('Dossier existe déjà.');
|
||||
|
||||
const result = await folderCollection.insertOne(folder);
|
||||
return result.insertedId;
|
||||
}
|
||||
|
||||
async getUserFolders(userId: string): Promise<any[]> {
|
||||
await db.connect();
|
||||
const conn = db.getConnection();
|
||||
|
||||
const foldersCollection = conn.collection('folders');
|
||||
|
||||
const result = await foldersCollection.find({ userId }).toArray();
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
async getOwner(folderId: string): Promise<string | null> {
|
||||
await this.db.connect();
|
||||
const conn: Db = this.db.getConnection();
|
||||
|
||||
const folderCollection = conn.collection('folders');
|
||||
|
||||
const folder = await folderCollection.findOne({ _id: new ObjectId(folderId) });
|
||||
|
||||
if (!folder) throw new Error('Dossier non trouvé.');
|
||||
|
||||
return folder.userId;
|
||||
}
|
||||
|
||||
async getContent(folderId: string): Promise<any[]> {
|
||||
await this.db.connect();
|
||||
const conn: Db = this.db.getConnection();
|
||||
|
||||
const filesCollection = conn.collection('files');
|
||||
|
||||
const result = await filesCollection.find({ folderId }).toArray();
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
async delete(folderId: string): Promise<boolean> {
|
||||
await this.db.connect();
|
||||
const conn: Db = this.db.getConnection();
|
||||
|
||||
const folderCollection = conn.collection('folders');
|
||||
|
||||
// can't delete a folder if it has quizzes in it
|
||||
const filesCollection = conn.collection('files');
|
||||
const quizzes = await filesCollection.find({ folderId }).toArray();
|
||||
if (quizzes.length > 0) throw new Error('Dossier non vide.');
|
||||
|
||||
const result = await folderCollection.deleteOne({ _id: new ObjectId(folderId) });
|
||||
|
||||
return result.deletedCount === 1;
|
||||
}
|
||||
|
||||
// async deleteQuizzes(folderId: string): Promise<number> {
|
||||
// await this.db.connect();
|
||||
// const conn: Db = this.db.getConnection();
|
||||
|
||||
// const quizCollection = conn.collection('files');
|
||||
|
||||
// const result = await quizCollection.deleteMany({ folderId: folderId });
|
||||
// return result.deletedCount || 0;
|
||||
// }
|
||||
|
||||
async rename(folderId: string, newTitle: string): Promise<boolean> {
|
||||
await this.db.connect();
|
||||
const conn: Db = this.db.getConnection();
|
||||
|
||||
const folderCollection = conn.collection('folders');
|
||||
|
||||
const result = await folderCollection.updateOne(
|
||||
{ _id: new ObjectId(folderId) },
|
||||
{ $set: { title: newTitle } }
|
||||
);
|
||||
|
||||
return result.modifiedCount === 1;
|
||||
}
|
||||
|
||||
async duplicate(folderId: string): Promise<ObjectId | null> {
|
||||
const sourceFolder = await this.getFolderWithContent(folderId);
|
||||
|
||||
let newFolderTitle = `${sourceFolder.title}-copie`;
|
||||
let counter = 1;
|
||||
|
||||
while (await sourceFolder.folderExists(newFolderTitle, sourceFolder.userId)) {
|
||||
newFolderTitle = `${sourceFolder.title}-copie(${counter})`;
|
||||
counter++;
|
||||
}
|
||||
|
||||
const newFolderId = await this.createFolder(newFolderTitle);
|
||||
|
||||
if (!newFolderId) {
|
||||
throw new Error('Failed to create a duplicate folder.');
|
||||
}
|
||||
|
||||
for (const quiz of sourceFolder.content) {
|
||||
const { title, content } = quiz;
|
||||
await Quiz.create(title, content, newFolderId.toString(), userId);
|
||||
}
|
||||
|
||||
return newFolderId;
|
||||
}
|
||||
|
||||
async quizExists(title: string, userId: string): Promise<boolean> {
|
||||
await this.db.connect();
|
||||
const conn: Db = this.db.getConnection();
|
||||
|
||||
const quizCollection = conn.collection('files');
|
||||
|
||||
const existingQuiz = await quizCollection.findOne({ title: title, userId: userId });
|
||||
|
||||
return existingQuiz !== null;
|
||||
}
|
||||
}
|
||||
|
||||
export default QuizRepository;
|
||||
42
server/services/imageService.ts
Normal file
42
server/services/imageService.ts
Normal file
|
|
@ -0,0 +1,42 @@
|
|||
import db from '../config/db';
|
||||
import { ObjectId } from 'mongodb';
|
||||
|
||||
export class Image {
|
||||
async upload(file: Express.Multer.File, userId: string): Promise<string> {
|
||||
await db.connect();
|
||||
const conn = db.getConnection();
|
||||
|
||||
const imagesCollection = conn.collection('images');
|
||||
|
||||
const newImage = {
|
||||
userId: userId,
|
||||
file_name: file.originalname,
|
||||
file_content: file.buffer.toString('base64'),
|
||||
mime_type: file.mimetype,
|
||||
created_at: new Date(),
|
||||
};
|
||||
|
||||
const result = await imagesCollection.insertOne(newImage);
|
||||
|
||||
return result.insertedId.toString();
|
||||
}
|
||||
|
||||
async get(id: string): Promise<{ file_name: string; file_content: Buffer; mime_type: string } | null> {
|
||||
await db.connect();
|
||||
const conn = db.getConnection();
|
||||
|
||||
const imagesCollection = conn.collection('images');
|
||||
|
||||
const result = await imagesCollection.findOne({ _id: new ObjectId(id) });
|
||||
|
||||
if (!result) return null;
|
||||
|
||||
return {
|
||||
file_name: result.file_name,
|
||||
file_content: Buffer.from(result.file_content, 'base64'),
|
||||
mime_type: result.mime_type,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
export default new Image();
|
||||
22
server/services/quizService.ts
Normal file
22
server/services/quizService.ts
Normal file
|
|
@ -0,0 +1,22 @@
|
|||
import { QuizRepository } from '../repositories/quizRepository';
|
||||
import { Quiz } from '../models/quiz';
|
||||
|
||||
// define a parameter object for the createQuiz method
|
||||
interface CreateQuizParams {
|
||||
title: string;
|
||||
content: string;
|
||||
folder: Folder;
|
||||
user: User;
|
||||
}
|
||||
|
||||
export class QuizService {
|
||||
constructor(private quizRepository: QuizRepository) {}
|
||||
|
||||
async createQuiz(params: CreateQuizParams): Promise<Quiz> {
|
||||
// Create a new Quiz object without an ID
|
||||
const quiz = new Quiz(params.folder, params.user, params.title, params.content);
|
||||
|
||||
// Save the quiz to the database, and the repository assigns the ID
|
||||
return this.quizRepository.save(quiz);
|
||||
}
|
||||
}
|
||||
Loading…
Reference in a new issue