mirror of
https://github.com/ets-cfuhrman-pfe/EvalueTonSavoir.git
synced 2025-08-11 21:23:54 -04:00
Merge pull request #147 from ets-cfuhrman-pfe/backend-tests
Backend tests and refactorings
This commit is contained in:
commit
b1e9489ba9
29 changed files with 1509 additions and 481 deletions
|
|
@ -1,4 +1,4 @@
|
|||
name: Frontend Tests
|
||||
name: Tests
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
|
|
@ -9,7 +9,7 @@ on:
|
|||
- main
|
||||
|
||||
jobs:
|
||||
frontend-test:
|
||||
tests:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
|
|
@ -19,12 +19,14 @@ jobs:
|
|||
- name: Set up Node.js
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: '18'
|
||||
node-version: '18'
|
||||
|
||||
- name: Install Dependencies
|
||||
run: npm ci
|
||||
working-directory: ./client
|
||||
- name: Install Dependencies and Run Tests
|
||||
run: |
|
||||
npm ci
|
||||
npm test
|
||||
working-directory: ${{ matrix.directory }}
|
||||
|
||||
- name: Run Tests
|
||||
run: npm test
|
||||
working-directory: ./client
|
||||
strategy:
|
||||
matrix:
|
||||
directory: [client, server]
|
||||
1
.gitignore
vendored
1
.gitignore
vendored
|
|
@ -128,3 +128,4 @@ dist
|
|||
.yarn/build-state.yml
|
||||
.yarn/install-state.gz
|
||||
.pnp.*
|
||||
db-backup/
|
||||
|
|
|
|||
|
|
@ -98,8 +98,9 @@ const Dashboard: React.FC = () => {
|
|||
setQuizzes(quizzes as QuizType[]);
|
||||
}
|
||||
else {
|
||||
console.log("show some quizes")
|
||||
console.log("show some quizzes")
|
||||
const folderQuizzes = await ApiService.getFolderContent(selectedFolder);
|
||||
console.log("folderQuizzes: ", folderQuizzes);
|
||||
setQuizzes(folderQuizzes as QuizType[]);
|
||||
|
||||
}
|
||||
|
|
@ -147,7 +148,7 @@ const Dashboard: React.FC = () => {
|
|||
setQuizzes(quizzes as QuizType[]);
|
||||
}
|
||||
else {
|
||||
console.log("show some quizes")
|
||||
console.log("show some quizzes")
|
||||
const folderQuizzes = await ApiService.getFolderContent(selectedFolder);
|
||||
setQuizzes(folderQuizzes as QuizType[]);
|
||||
|
||||
|
|
@ -292,7 +293,7 @@ const Dashboard: React.FC = () => {
|
|||
}
|
||||
|
||||
setQuizzes(quizzes as QuizType[]);
|
||||
|
||||
setSelectedFolder('');
|
||||
|
||||
} catch (error) {
|
||||
console.error('Error deleting folder:', error);
|
||||
|
|
@ -317,9 +318,11 @@ const Dashboard: React.FC = () => {
|
|||
try {
|
||||
// folderId: string GET THIS FROM CURRENT FOLDER
|
||||
await ApiService.duplicateFolder(selectedFolder);
|
||||
// TODO set the selected folder to be the duplicated folder
|
||||
const userFolders = await ApiService.getUserFolders();
|
||||
setFolders(userFolders as FolderType[]);
|
||||
|
||||
const newlyCreatedFolder = userFolders[userFolders.length - 1] as FolderType;
|
||||
setSelectedFolder(newlyCreatedFolder._id);
|
||||
} catch (error) {
|
||||
console.error('Error duplicating folder:', error);
|
||||
}
|
||||
|
|
@ -401,7 +404,6 @@ const Dashboard: React.FC = () => {
|
|||
</div>
|
||||
|
||||
<div className='actions'>
|
||||
|
||||
<Tooltip title="Ajouter dossier" placement="top">
|
||||
<IconButton
|
||||
color="primary"
|
||||
|
|
@ -425,7 +427,7 @@ const Dashboard: React.FC = () => {
|
|||
> <ContentCopy /> </IconButton>
|
||||
</Tooltip>
|
||||
|
||||
<Tooltip title="Suprimer dossier" placement="top">
|
||||
<Tooltip title="Supprimer dossier" placement="top">
|
||||
<IconButton
|
||||
aria-label="delete"
|
||||
color="primary"
|
||||
|
|
|
|||
8
server/__mocks__/AppError.js
Normal file
8
server/__mocks__/AppError.js
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
class AppError extends Error {
|
||||
constructor(message, statusCode) {
|
||||
super(message);
|
||||
this.statusCode = statusCode;
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = AppError;
|
||||
6
server/__mocks__/bcrypt.js
Normal file
6
server/__mocks__/bcrypt.js
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
const mockBcrypt = {
|
||||
hash: jest.fn().mockResolvedValue('hashedPassword'),
|
||||
compare: jest.fn().mockResolvedValue(true),
|
||||
};
|
||||
|
||||
module.exports = mockBcrypt;
|
||||
20
server/__mocks__/db.js
Normal file
20
server/__mocks__/db.js
Normal file
|
|
@ -0,0 +1,20 @@
|
|||
class MockDBConnection {
|
||||
constructor() {
|
||||
this.db = jest.fn().mockReturnThis();
|
||||
this.collection = jest.fn().mockReturnThis();
|
||||
this.insertOne = jest.fn();
|
||||
this.findOne = jest.fn();
|
||||
this.updateOne = jest.fn();
|
||||
this.deleteOne = jest.fn();
|
||||
}
|
||||
|
||||
async connect() {
|
||||
// Simulate successful connection
|
||||
}
|
||||
|
||||
getConnection() {
|
||||
return this;
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = MockDBConnection;
|
||||
8
server/__mocks__/folders.js
Normal file
8
server/__mocks__/folders.js
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
const mockFolders = {
|
||||
create: jest.fn(),
|
||||
find: jest.fn(),
|
||||
update: jest.fn(),
|
||||
delete: jest.fn(),
|
||||
};
|
||||
|
||||
module.exports = mockFolders;
|
||||
441
server/__tests__/folders.test.js
Normal file
441
server/__tests__/folders.test.js
Normal file
|
|
@ -0,0 +1,441 @@
|
|||
const { create } = require('../middleware/jwtToken');
|
||||
const Folders = require('../models/folders');
|
||||
const ObjectId = require('mongodb').ObjectId;
|
||||
const Quizzes = require('../models/quiz');
|
||||
|
||||
describe('Folders', () => {
|
||||
let folders;
|
||||
let db;
|
||||
let collection;
|
||||
let quizzes;
|
||||
|
||||
beforeEach(() => {
|
||||
jest.clearAllMocks(); // Clear any previous mock calls
|
||||
|
||||
// Mock the collection object
|
||||
collection = {
|
||||
findOne: jest.fn(),
|
||||
insertOne: jest.fn(),
|
||||
find: jest.fn().mockReturnValue({ toArray: jest.fn() }), // Mock the find method
|
||||
deleteOne: jest.fn(),
|
||||
deleteMany: jest.fn(),
|
||||
updateOne: jest.fn(),
|
||||
};
|
||||
|
||||
// Mock the database connection
|
||||
db = {
|
||||
connect: jest.fn(),
|
||||
getConnection: jest.fn().mockReturnThis(), // Add getConnection method
|
||||
collection: jest.fn().mockReturnValue(collection),
|
||||
};
|
||||
|
||||
quizzes = new Quizzes(db);
|
||||
folders = new Folders(db, quizzes);
|
||||
|
||||
});
|
||||
|
||||
// create
|
||||
describe('create', () => {
|
||||
it('should create a new folder and return the new folder ID', async () => {
|
||||
const title = 'Test Folder';
|
||||
|
||||
// Mock the database response
|
||||
collection.findOne.mockResolvedValue(null);
|
||||
collection.insertOne.mockResolvedValue({ insertedId: new ObjectId() });
|
||||
|
||||
const result = await folders.create(title, '12345');
|
||||
|
||||
expect(db.connect).toHaveBeenCalled();
|
||||
expect(db.collection).toHaveBeenCalledWith('folders');
|
||||
expect(collection.findOne).toHaveBeenCalledWith({ title, userId: '12345' });
|
||||
expect(collection.insertOne).toHaveBeenCalledWith(expect.objectContaining({ title, userId: '12345' }));
|
||||
expect(result).toBeDefined();
|
||||
});
|
||||
|
||||
// throw an error if userId is undefined
|
||||
it('should throw an error if userId is undefined', async () => {
|
||||
const title = 'Test Folder';
|
||||
|
||||
await expect(folders.create(title, undefined)).rejects.toThrow('Missing required parameter(s)');
|
||||
|
||||
expect(db.connect).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should throw an error if the folder already exists', async () => {
|
||||
const title = 'Existing Folder';
|
||||
const userId = '66fc70bea1b9e87655cf17c9';
|
||||
|
||||
// Mock the database response of a found folder
|
||||
collection.findOne.mockResolvedValue(
|
||||
// real result from mongosh
|
||||
{
|
||||
_id: ObjectId.createFromHexString('66fd33fd81758a882ce99aae'),
|
||||
userId: userId,
|
||||
title: title,
|
||||
created_at: new Date('2024-10-02T11:52:29.797Z')
|
||||
}
|
||||
);
|
||||
|
||||
await expect(folders.create(title, userId)).rejects.toThrow('Folder already exists');
|
||||
|
||||
expect(db.connect).toHaveBeenCalled();
|
||||
expect(db.collection).toHaveBeenCalledWith('folders');
|
||||
expect(collection.findOne).toHaveBeenCalledWith({ title, userId: userId });
|
||||
});
|
||||
});
|
||||
|
||||
// getUserFolders
|
||||
describe('getUserFolders', () => {
|
||||
it('should return all folders for a user', async () => {
|
||||
const userId = '12345';
|
||||
const userFolders = [
|
||||
{ title: 'Folder 1', userId },
|
||||
{ title: 'Folder 2', userId },
|
||||
];
|
||||
|
||||
// Mock the database response
|
||||
collection.find().toArray.mockResolvedValue(userFolders);
|
||||
|
||||
const result = await folders.getUserFolders(userId);
|
||||
|
||||
expect(db.connect).toHaveBeenCalled();
|
||||
expect(db.collection).toHaveBeenCalledWith('folders');
|
||||
expect(collection.find).toHaveBeenCalledWith({ userId });
|
||||
expect(result).toEqual(userFolders);
|
||||
});
|
||||
});
|
||||
|
||||
// getOwner
|
||||
describe('getOwner', () => {
|
||||
it('should return the owner of a folder', async () => {
|
||||
const folderId = '60c72b2f9b1d8b3a4c8e4d3b';
|
||||
const userId = '12345';
|
||||
|
||||
// Mock the database response
|
||||
collection.findOne.mockResolvedValue({ userId });
|
||||
|
||||
const result = await folders.getOwner(folderId);
|
||||
|
||||
expect(db.connect).toHaveBeenCalled();
|
||||
expect(db.collection).toHaveBeenCalledWith('folders');
|
||||
expect(collection.findOne).toHaveBeenCalledWith({ _id: new ObjectId(folderId) });
|
||||
expect(result).toBe(userId);
|
||||
});
|
||||
});
|
||||
|
||||
// write a test for getContent
|
||||
describe('getContent', () => {
|
||||
it('should return the content of a folder', async () => {
|
||||
const folderId = '60c72b2f9b1d8b3a4c8e4d3b';
|
||||
const content = [
|
||||
{ title: 'Quiz 1', content: [] },
|
||||
{ title: 'Quiz 2', content: [] },
|
||||
];
|
||||
|
||||
// Mock the database response
|
||||
collection.find().toArray.mockResolvedValue(content);
|
||||
|
||||
const result = await folders.getContent(folderId);
|
||||
|
||||
expect(db.connect).toHaveBeenCalled();
|
||||
expect(db.collection).toHaveBeenCalledWith('files');
|
||||
expect(collection.find).toHaveBeenCalledWith({ folderId });
|
||||
expect(result).toEqual(content);
|
||||
});
|
||||
|
||||
it('should return an empty array if the folder has no content', async () => {
|
||||
const folderId = '60c72b2f9b1d8b3a4c8e4d3b';
|
||||
|
||||
// Mock the database response
|
||||
collection.find().toArray.mockResolvedValue([]);
|
||||
|
||||
const result = await folders.getContent(folderId);
|
||||
|
||||
expect(db.connect).toHaveBeenCalled();
|
||||
expect(db.collection).toHaveBeenCalledWith('files');
|
||||
expect(collection.find).toHaveBeenCalledWith({ folderId });
|
||||
expect(result).toEqual([]);
|
||||
});
|
||||
});
|
||||
|
||||
// delete
|
||||
describe('delete', () => {
|
||||
it('should delete a folder and return true', async () => {
|
||||
const folderId = '60c72b2f9b1d8b3a4c8e4d3b';
|
||||
|
||||
// Mock the database response
|
||||
collection.deleteOne.mockResolvedValue({ deletedCount: 1 });
|
||||
|
||||
|
||||
// Mock the folders.quizModel.deleteQuizzesByFolderId()
|
||||
jest.spyOn(quizzes, 'deleteQuizzesByFolderId').mockResolvedValue(true);
|
||||
|
||||
const result = await folders.delete(folderId);
|
||||
|
||||
expect(db.connect).toHaveBeenCalled();
|
||||
expect(db.collection).toHaveBeenCalledWith('folders');
|
||||
expect(collection.deleteOne).toHaveBeenCalledWith({ _id: new ObjectId(folderId) });
|
||||
expect(result).toBe(true);
|
||||
});
|
||||
|
||||
it('should return false if the folder does not exist', async () => {
|
||||
const folderId = '60c72b2f9b1d8b3a4c8e4d3b';
|
||||
|
||||
// Mock the database response
|
||||
collection.deleteOne.mockResolvedValue({ deletedCount: 0 });
|
||||
|
||||
const result = await folders.delete(folderId);
|
||||
|
||||
expect(db.connect).toHaveBeenCalled();
|
||||
expect(db.collection).toHaveBeenCalledWith('folders');
|
||||
expect(collection.deleteOne).toHaveBeenCalledWith({ _id: new ObjectId(folderId) });
|
||||
expect(result).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
// rename
|
||||
describe('rename', () => {
|
||||
it('should rename a folder and return true', async () => {
|
||||
const folderId = '60c72b2f9b1d8b3a4c8e4d3b';
|
||||
const newTitle = 'New Folder Name';
|
||||
|
||||
// Mock the database response
|
||||
collection.updateOne.mockResolvedValue({ modifiedCount: 1 });
|
||||
|
||||
const result = await folders.rename(folderId, newTitle);
|
||||
|
||||
expect(db.connect).toHaveBeenCalled();
|
||||
expect(db.collection).toHaveBeenCalledWith('folders');
|
||||
expect(collection.updateOne).toHaveBeenCalledWith({ _id: new ObjectId(folderId) }, { $set: { title: newTitle } });
|
||||
expect(result).toBe(true);
|
||||
});
|
||||
|
||||
it('should return false if the folder does not exist', async () => {
|
||||
const folderId = '60c72b2f9b1d8b3a4c8e4d3b';
|
||||
const newTitle = 'New Folder Name';
|
||||
|
||||
// Mock the database response
|
||||
collection.updateOne.mockResolvedValue({ modifiedCount: 0 });
|
||||
|
||||
const result = await folders.rename(folderId, newTitle);
|
||||
|
||||
expect(db.connect).toHaveBeenCalled();
|
||||
expect(db.collection).toHaveBeenCalledWith('folders');
|
||||
expect(collection.updateOne).toHaveBeenCalledWith({ _id: new ObjectId(folderId) }, { $set: { title: newTitle } });
|
||||
expect(result).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
// duplicate
|
||||
describe('duplicate', () => {
|
||||
it('should duplicate a folder and return the new folder ID', async () => {
|
||||
const userId = '12345';
|
||||
const folderId = '60c72b2f9b1d8b3a4c8e4d3b';
|
||||
const sourceFolder = {title: 'SourceFolder', userId: userId, content: []};
|
||||
const duplicatedFolder = {title: 'SourceFolder (1)', userId: userId, created_at: expect.any(Date), content: []};
|
||||
|
||||
// Mock the database responses for the folder and the new folder (first one is found, second one is null)
|
||||
// mock the findOne method
|
||||
jest.spyOn(collection, 'findOne')
|
||||
.mockResolvedValueOnce(sourceFolder) // source file exists
|
||||
.mockResolvedValueOnce(null); // new name is not found
|
||||
|
||||
// Mock the folder create method
|
||||
const createSpy = jest.spyOn(folders, 'create').mockResolvedValue(new ObjectId());
|
||||
|
||||
// mock the folder.getContent method
|
||||
jest.spyOn(folders, 'getContent').mockResolvedValue([{ title: 'Quiz 1', content: [] }]);
|
||||
|
||||
// Mock the quizzes.create method
|
||||
jest.spyOn(quizzes, 'create').mockResolvedValue(new ObjectId());
|
||||
|
||||
const result = await folders.duplicate(folderId, userId);
|
||||
|
||||
expect(db.collection).toHaveBeenCalledWith('folders');
|
||||
|
||||
// expect folders.create method was called
|
||||
expect(createSpy).toHaveBeenCalledWith(duplicatedFolder.title, userId);
|
||||
// expect the getContent method was called
|
||||
expect(folders.getContent).toHaveBeenCalledWith(folderId);
|
||||
// expect the quizzes.create method was called
|
||||
expect(quizzes.create).toHaveBeenCalledWith('Quiz 1', [], expect.any(String), userId);
|
||||
|
||||
expect(result).toBeDefined();
|
||||
});
|
||||
|
||||
it('should throw an error if the folder does not exist', async () => {
|
||||
const folderId = '60c72b2f9b1d8b3a4c8e4d3b';
|
||||
|
||||
// Mock the database response for the source
|
||||
collection.findOne.mockResolvedValue(null);
|
||||
|
||||
await expect(folders.duplicate(folderId, '54321')).rejects.toThrow(`Folder ${folderId} not found`);
|
||||
|
||||
// expect(db.connect).toHaveBeenCalled();
|
||||
expect(db.collection).toHaveBeenCalledWith('folders');
|
||||
expect(collection.findOne).toHaveBeenCalledWith({ _id: new ObjectId(folderId), userId: '54321' });
|
||||
});
|
||||
});
|
||||
|
||||
describe('folderExists', () => {
|
||||
it('should return true if folder exists', async () => {
|
||||
const title = 'Test Folder';
|
||||
const userId = '12345';
|
||||
|
||||
// Mock the database response
|
||||
collection.findOne.mockResolvedValue({ title, userId });
|
||||
|
||||
const result = await folders.folderExists(title, userId);
|
||||
|
||||
expect(db.connect).toHaveBeenCalled();
|
||||
expect(db.collection).toHaveBeenCalledWith('folders');
|
||||
expect(collection.findOne).toHaveBeenCalledWith({ title, userId });
|
||||
expect(result).toBe(true);
|
||||
});
|
||||
|
||||
it('should return false if folder does not exist', async () => {
|
||||
const title = 'Nonexistent Folder';
|
||||
const userId = '12345';
|
||||
|
||||
// Mock the database response
|
||||
collection.findOne.mockResolvedValue(null);
|
||||
|
||||
const result = await folders.folderExists(title, userId);
|
||||
|
||||
expect(db.connect).toHaveBeenCalled();
|
||||
expect(db.collection).toHaveBeenCalledWith('folders');
|
||||
expect(collection.findOne).toHaveBeenCalledWith({ title, userId });
|
||||
expect(result).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
describe('copy', () => {
|
||||
it('should copy a folder and return the new folder ID', async () => {
|
||||
const folderId = '60c72b2f9b1d8b3a4c8e4d3b';
|
||||
const userId = '12345';
|
||||
const newFolderId = new ObjectId();
|
||||
// Mock some quizzes that are in folder.content
|
||||
const sourceFolder = {
|
||||
title: 'Test Folder',
|
||||
content: [
|
||||
{ title: 'Quiz 1', content: [] },
|
||||
{ title: 'Quiz 2', content: [] },
|
||||
],
|
||||
};
|
||||
|
||||
// Mock the response from getFolderWithContent
|
||||
jest.spyOn(folders, 'getFolderWithContent').mockResolvedValue(sourceFolder);
|
||||
jest.spyOn(folders, 'create').mockResolvedValue(newFolderId);
|
||||
// Mock the response from Quiz.createQuiz
|
||||
jest.spyOn(quizzes, 'create').mockImplementation(() => {});
|
||||
|
||||
const result = await folders.copy(folderId, userId);
|
||||
|
||||
// expect(db.connect).toHaveBeenCalled();
|
||||
// expect(db.collection).toHaveBeenCalledWith('folders');
|
||||
// expect(collection.findOne).toHaveBeenCalledWith({ _id: new ObjectId(folderId) });
|
||||
// expect(collection.insertOne).toHaveBeenCalledWith(expect.objectContaining({ userId }));
|
||||
expect(result).toBe(newFolderId);
|
||||
});
|
||||
|
||||
it('should throw an error if the folder does not exist', async () => {
|
||||
const folderId = '60c72b2f9b1d8b3a4c8e4d3b';
|
||||
const userId = '12345';
|
||||
|
||||
// Mock the response from getFolderWithContent
|
||||
jest.spyOn(folders, 'getFolderWithContent').mockImplementation(() => {
|
||||
throw new Error(`Folder ${folderId} not found`);
|
||||
});
|
||||
|
||||
await expect(folders.copy(folderId, userId)).rejects.toThrow(`Folder ${folderId} not found`);
|
||||
|
||||
// expect(db.connect).toHaveBeenCalled();
|
||||
// expect(db.collection).toHaveBeenCalledWith('folders');
|
||||
// expect(collection.findOne).toHaveBeenCalledWith({ _id: new ObjectId(folderId) });
|
||||
});
|
||||
});
|
||||
|
||||
// write a test for getFolderWithContent
|
||||
describe('getFolderWithContent', () => {
|
||||
it('should return a folder with content', async () => {
|
||||
const folderId = '60c72b2f9b1d8b3a4c8e4d3b';
|
||||
const folder = {
|
||||
_id: new ObjectId(folderId),
|
||||
title: 'Test Folder',
|
||||
};
|
||||
const content = {
|
||||
content : [
|
||||
{ title: 'Quiz 1', content: [] },
|
||||
{ title: 'Quiz 2', content: [] },
|
||||
]};
|
||||
|
||||
// Mock the response from getFolderById
|
||||
jest.spyOn(folders, 'getFolderById').mockResolvedValue(folder);
|
||||
|
||||
// Mock the response from getContent
|
||||
jest.spyOn(folders, 'getContent').mockResolvedValue(content);
|
||||
|
||||
const result = await folders.getFolderWithContent(folderId);
|
||||
|
||||
// expect(db.connect).toHaveBeenCalled();
|
||||
// expect(db.collection).toHaveBeenCalledWith('folders');
|
||||
// expect(collection.findOne).toHaveBeenCalledWith({ _id: new ObjectId(folderId) });
|
||||
expect(result).toEqual({
|
||||
...folder,
|
||||
content: content
|
||||
});
|
||||
});
|
||||
|
||||
it('should throw an error if the folder does not exist', async () => {
|
||||
const folderId = '60c72b2f9b1d8b3a4c8e4d3b';
|
||||
|
||||
// // Mock the database response
|
||||
// collection.findOne.mockResolvedValue(null);
|
||||
|
||||
// Mock getFolderById to throw an error
|
||||
jest.spyOn(folders, 'getFolderById').mockImplementation(() => {
|
||||
throw new Error(`Folder ${folderId} not found`);
|
||||
});
|
||||
|
||||
await expect(folders.getFolderWithContent(folderId)).rejects.toThrow(`Folder ${folderId} not found`);
|
||||
|
||||
// expect(db.connect).toHaveBeenCalled();
|
||||
// expect(db.collection).toHaveBeenCalledWith('folders');
|
||||
// expect(collection.findOne).toHaveBeenCalledWith({ _id: new ObjectId(folderId) });
|
||||
});
|
||||
});
|
||||
|
||||
// write a test for getFolderById
|
||||
describe('getFolderById', () => {
|
||||
it('should return a folder by ID', async () => {
|
||||
const folderId = '60c72b2f9b1d8b3a4c8e4d3b';
|
||||
const folder = {
|
||||
_id: new ObjectId(folderId),
|
||||
title: 'Test Folder',
|
||||
};
|
||||
|
||||
// Mock the database response
|
||||
collection.findOne.mockResolvedValue(folder);
|
||||
|
||||
const result = await folders.getFolderById(folderId);
|
||||
|
||||
expect(db.connect).toHaveBeenCalled();
|
||||
expect(db.collection).toHaveBeenCalledWith('folders');
|
||||
expect(collection.findOne).toHaveBeenCalledWith({ _id: new ObjectId(folderId) });
|
||||
expect(result).toEqual(folder);
|
||||
});
|
||||
|
||||
it('should throw an error if the folder does not exist', async () => {
|
||||
const folderId = '60c72b2f9b1d8b3a4c8e4d3b';
|
||||
|
||||
// Mock the database response
|
||||
collection.findOne.mockResolvedValue(null);
|
||||
|
||||
await expect(folders.getFolderById(folderId)).resolves.toThrow(`Folder ${folderId} not found`);
|
||||
|
||||
expect(db.connect).toHaveBeenCalled();
|
||||
expect(db.collection).toHaveBeenCalledWith('folders');
|
||||
expect(collection.findOne).toHaveBeenCalledWith({ _id: new ObjectId(folderId) });
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
@ -1,11 +1,11 @@
|
|||
const request = require('supertest');
|
||||
const app = require('../app.js');
|
||||
// const app = require('../routers/images.js');
|
||||
const { response } = require('express');
|
||||
// const request = require('supertest');
|
||||
// const app = require('../app.js');
|
||||
// // const app = require('../routers/images.js');
|
||||
// const { response } = require('express');
|
||||
|
||||
const BASE_URL = '/image'
|
||||
// const BASE_URL = '/image'
|
||||
|
||||
describe("POST /upload", () => {
|
||||
describe.skip("POST /upload", () => {
|
||||
|
||||
describe("when the jwt is not sent", () => {
|
||||
|
||||
|
|
@ -44,7 +44,7 @@ describe("POST /upload", () => {
|
|||
|
||||
})
|
||||
|
||||
describe("GET /get", () => {
|
||||
describe.skip("GET /get", () => {
|
||||
|
||||
describe("when not give id", () => {
|
||||
|
||||
|
|
@ -61,4 +61,4 @@ describe("GET /get", () => {
|
|||
|
||||
})
|
||||
|
||||
})
|
||||
})
|
||||
|
|
|
|||
347
server/__tests__/quizzes.test.js
Normal file
347
server/__tests__/quizzes.test.js
Normal file
|
|
@ -0,0 +1,347 @@
|
|||
const { ObjectId } = require('mongodb');
|
||||
const Quizzes = require('../models/quiz'); // Adjust the path as necessary
|
||||
|
||||
describe('Quizzes', () => {
|
||||
let db;
|
||||
let quizzes;
|
||||
let collection;
|
||||
|
||||
beforeEach(() => {
|
||||
jest.clearAllMocks(); // Clear any previous mock calls
|
||||
|
||||
// Mock the collection object
|
||||
collection = {
|
||||
findOne: jest.fn(),
|
||||
insertOne: jest.fn(),
|
||||
find: jest.fn().mockReturnValue({ toArray: jest.fn() }), // Mock the find method
|
||||
deleteOne: jest.fn(),
|
||||
deleteMany: jest.fn(),
|
||||
updateOne: jest.fn(),
|
||||
getContent: jest.fn(),
|
||||
};
|
||||
|
||||
// Mock the database connection
|
||||
db = {
|
||||
connect: jest.fn(),
|
||||
getConnection: jest.fn().mockReturnValue({
|
||||
collection: jest.fn().mockReturnValue(collection),
|
||||
}),
|
||||
};
|
||||
|
||||
// Initialize the Quiz model with the mocked db
|
||||
quizzes = new Quizzes(db);
|
||||
});
|
||||
|
||||
describe('create', () => {
|
||||
it('should create a new quiz if it does not exist', async () => {
|
||||
const title = 'Test Quiz';
|
||||
const content = 'This is a test quiz.';
|
||||
const folderId = '507f1f77bcf86cd799439011';
|
||||
const userId = '12345';
|
||||
|
||||
// Mock the database response
|
||||
collection.findOne.mockResolvedValue(null);
|
||||
collection.insertOne.mockResolvedValue({ insertedId: new ObjectId() });
|
||||
|
||||
const result = await quizzes.create(title, content, folderId, userId);
|
||||
|
||||
expect(db.connect).toHaveBeenCalled();
|
||||
expect(db.getConnection).toHaveBeenCalled();
|
||||
expect(collection.findOne).toHaveBeenCalledWith({ title, folderId, userId });
|
||||
expect(collection.insertOne).toHaveBeenCalledWith(expect.objectContaining({
|
||||
folderId,
|
||||
userId,
|
||||
title,
|
||||
content,
|
||||
created_at: expect.any(Date),
|
||||
updated_at: expect.any(Date),
|
||||
}));
|
||||
expect(result).not.toBeNull();
|
||||
});
|
||||
|
||||
it('should throw exception if the quiz already exists', async () => {
|
||||
const title = 'Test Quiz';
|
||||
const content = 'This is a test quiz.';
|
||||
const folderId = '507f1f77bcf86cd799439011';
|
||||
const userId = '12345';
|
||||
|
||||
// Mock the database response
|
||||
collection.findOne.mockResolvedValue({ title });
|
||||
|
||||
await expect(quizzes.create(title, content, folderId, userId)).rejects.toThrow(`Quiz already exists with title: ${title}, folderId: ${folderId}, userId: ${userId}`);
|
||||
});
|
||||
});
|
||||
|
||||
describe('getOwner', () => {
|
||||
it('should return the owner of the quiz', async () => {
|
||||
const quizId = '60c72b2f9b1d8b3a4c8e4d3b';
|
||||
const userId = '12345';
|
||||
|
||||
// Mock the database response
|
||||
collection.findOne.mockResolvedValue({ userId });
|
||||
|
||||
const result = await quizzes.getOwner(quizId);
|
||||
|
||||
expect(db.connect).toHaveBeenCalled();
|
||||
expect(db.getConnection).toHaveBeenCalled();
|
||||
expect(collection.findOne).toHaveBeenCalledWith({ _id: ObjectId.createFromHexString(quizId) });
|
||||
expect(result).toBe(userId);
|
||||
});
|
||||
});
|
||||
|
||||
describe('getContent', () => {
|
||||
it('should return the content of the quiz', async () => {
|
||||
const quizId = '60c72b2f9b1d8b3a4c8e4d3b';
|
||||
const content = 'This is a test quiz.';
|
||||
|
||||
// Mock the database response
|
||||
collection.findOne.mockResolvedValue({ content });
|
||||
|
||||
const result = await quizzes.getContent(quizId);
|
||||
|
||||
expect(db.connect).toHaveBeenCalled();
|
||||
expect(db.getConnection).toHaveBeenCalled();
|
||||
expect(collection.findOne).toHaveBeenCalledWith({ _id: ObjectId.createFromHexString(quizId) });
|
||||
expect(result).toEqual({ content });
|
||||
});
|
||||
});
|
||||
|
||||
describe('delete', () => {
|
||||
it('should delete the quiz', async () => {
|
||||
const quizId = '60c72b2f9b1d8b3a4c8e4d3b';
|
||||
|
||||
// Mock the database response
|
||||
collection.deleteOne.mockResolvedValue({deletedCount: 1});
|
||||
|
||||
await quizzes.delete(quizId);
|
||||
|
||||
expect(db.connect).toHaveBeenCalled();
|
||||
expect(db.getConnection).toHaveBeenCalled();
|
||||
expect(collection.deleteOne).toHaveBeenCalledWith({ _id: ObjectId.createFromHexString(quizId) });
|
||||
});
|
||||
|
||||
it('should return false if the quiz does not exist', async () => {
|
||||
const quizId = '60c72b2f9b1d8b3a4c8e4d3b';
|
||||
|
||||
// Mock the database response
|
||||
collection.deleteOne.mockResolvedValue({deletedCount: 0});
|
||||
|
||||
const result = await quizzes.delete(quizId);
|
||||
|
||||
expect(db.connect).toHaveBeenCalled();
|
||||
expect(db.getConnection).toHaveBeenCalled();
|
||||
expect(collection.deleteOne).toHaveBeenCalledWith({ _id: ObjectId.createFromHexString(quizId) });
|
||||
expect(result).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
// deleteQuizzesByFolderId
|
||||
describe('deleteQuizzesByFolderId', () => {
|
||||
it('should delete all quizzes in a folder', async () => {
|
||||
const folderId = '60c72b2f9b1d8b3a4c8e4d3b';
|
||||
|
||||
// Mock the database response
|
||||
collection.deleteMany.mockResolvedValue({deletedCount: 2});
|
||||
|
||||
await quizzes.deleteQuizzesByFolderId(folderId);
|
||||
|
||||
expect(db.connect).toHaveBeenCalled();
|
||||
expect(db.getConnection).toHaveBeenCalled();
|
||||
expect(collection.deleteMany).toHaveBeenCalledWith({ folderId });
|
||||
});
|
||||
|
||||
it('should return false if no quizzes are deleted', async () => {
|
||||
const folderId = '60c72b2f9b1d8b3a4c8e4d3b';
|
||||
|
||||
// Mock the database response
|
||||
collection.deleteMany.mockResolvedValue({deletedCount: 0});
|
||||
|
||||
const result = await quizzes.deleteQuizzesByFolderId(folderId);
|
||||
|
||||
expect(db.connect).toHaveBeenCalled();
|
||||
expect(db.getConnection).toHaveBeenCalled();
|
||||
expect(collection.deleteMany).toHaveBeenCalledWith({ folderId });
|
||||
expect(result).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
// update
|
||||
describe('update', () => {
|
||||
it('should update the title and content of the quiz', async () => {
|
||||
const quizId = '60c72b2f9b1d8b3a4c8e4d3b';
|
||||
const newTitle = 'Updated Quiz';
|
||||
const newContent = 'This is an updated quiz.';
|
||||
|
||||
// Mock the database response
|
||||
collection.updateOne.mockResolvedValue({modifiedCount: 1});
|
||||
|
||||
await quizzes.update(quizId, newTitle, newContent);
|
||||
|
||||
expect(db.connect).toHaveBeenCalled();
|
||||
expect(db.getConnection).toHaveBeenCalled();
|
||||
expect(collection.updateOne).toHaveBeenCalledWith(
|
||||
{ _id: ObjectId.createFromHexString(quizId) },
|
||||
{ $set: { title: newTitle, content: newContent, updated_at: expect.any(Date) } }
|
||||
);
|
||||
});
|
||||
|
||||
it('should return false if the quiz does not exist', async () => {
|
||||
const quizId = '60c72b2f9b1d8b3a4c8e4d3b';
|
||||
const newTitle = 'Updated Quiz';
|
||||
const newContent = 'This is an updated quiz.';
|
||||
|
||||
// Mock the database response
|
||||
collection.updateOne.mockResolvedValue({modifiedCount: 0});
|
||||
|
||||
const result = await quizzes.update(quizId, newTitle, newContent);
|
||||
|
||||
expect(db.connect).toHaveBeenCalled();
|
||||
expect(db.getConnection).toHaveBeenCalled();
|
||||
expect(collection.updateOne).toHaveBeenCalledWith(
|
||||
{ _id: ObjectId.createFromHexString(quizId) },
|
||||
{ $set: { title: newTitle, content: newContent, updated_at: expect.any(Date) } }
|
||||
);
|
||||
expect(result).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
// move
|
||||
describe('move', () => {
|
||||
it('should move the quiz to a new folder', async () => {
|
||||
const quizId = '60c72b2f9b1d8b3a4c8e4d3b';
|
||||
const newFolderId = '507f1f77bcf86cd799439011';
|
||||
|
||||
// Mock the database response
|
||||
collection.updateOne.mockResolvedValue({modifiedCount: 1});
|
||||
|
||||
await quizzes.move(quizId, newFolderId);
|
||||
|
||||
expect(db.connect).toHaveBeenCalled();
|
||||
expect(db.getConnection).toHaveBeenCalled();
|
||||
expect(collection.updateOne).toHaveBeenCalledWith(
|
||||
{ _id: ObjectId.createFromHexString(quizId) },
|
||||
{ $set: { folderId: newFolderId } }
|
||||
);
|
||||
});
|
||||
|
||||
it('should return false if the quiz does not exist', async () => {
|
||||
const quizId = '60c72b2f9b1d8b3a4c8e4d3b';
|
||||
const newFolderId = '507f1f77bcf86cd799439011';
|
||||
|
||||
// Mock the database response
|
||||
collection.updateOne.mockResolvedValue({modifiedCount: 0});
|
||||
|
||||
const result = await quizzes.move(quizId, newFolderId);
|
||||
|
||||
expect(db.connect).toHaveBeenCalled();
|
||||
expect(db.getConnection).toHaveBeenCalled();
|
||||
expect(collection.updateOne).toHaveBeenCalledWith(
|
||||
{ _id: ObjectId.createFromHexString(quizId) },
|
||||
{ $set: { folderId: newFolderId } }
|
||||
);
|
||||
expect(result).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
// duplicate
|
||||
describe('duplicate', () => {
|
||||
|
||||
it('should duplicate the quiz and return the new quiz ID', async () => {
|
||||
const quizId = '60c72b2f9b1d8b3a4c8e4d3b';
|
||||
const userId = '12345';
|
||||
const newQuizId = ObjectId.createFromTime(Math.floor(Date.now() / 1000)); // Corrected ObjectId creation
|
||||
const sourceQuiz = {
|
||||
title: 'Test Quiz',
|
||||
content: 'This is a test quiz.',
|
||||
};
|
||||
|
||||
const createMock = jest.spyOn(quizzes, 'create').mockResolvedValue(newQuizId);
|
||||
// mock the findOne method
|
||||
jest.spyOn(collection, 'findOne')
|
||||
.mockResolvedValueOnce(sourceQuiz) // source quiz exists
|
||||
.mockResolvedValueOnce(null); // new name is not found
|
||||
|
||||
const result = await quizzes.duplicate(quizId, userId);
|
||||
|
||||
expect(result).toBe(newQuizId);
|
||||
|
||||
// Ensure mocks were called correctly
|
||||
expect(createMock).toHaveBeenCalledWith(
|
||||
sourceQuiz.title + ' (1)',
|
||||
sourceQuiz.content,
|
||||
undefined,
|
||||
userId
|
||||
);
|
||||
});
|
||||
|
||||
// Add test case for quizExists (name with number in parentheses)
|
||||
it('should create a new title if the quiz title already exists and ends with " (1)"', async () => {
|
||||
const quizId = '60c72b2f9b1d8b3a4c8e4d3b';
|
||||
const userId = '12345';
|
||||
const newQuizId = ObjectId.createFromTime(Math.floor(Date.now() / 1000));
|
||||
const sourceQuiz = {
|
||||
title: 'Test Quiz (1)',
|
||||
content: 'This is a test quiz.',
|
||||
};
|
||||
|
||||
const createMock = jest.spyOn(quizzes, 'create').mockResolvedValue(newQuizId);
|
||||
// mock the findOne method
|
||||
jest.spyOn(collection, 'findOne')
|
||||
.mockResolvedValueOnce(sourceQuiz) // source quiz exists
|
||||
.mockResolvedValueOnce(null); // new name is not found
|
||||
|
||||
const result = await quizzes.duplicate(quizId, userId);
|
||||
|
||||
expect(result).toBe(newQuizId);
|
||||
|
||||
// Ensure mocks were called correctly
|
||||
expect(createMock).toHaveBeenCalledWith(
|
||||
'Test Quiz (2)',
|
||||
sourceQuiz.content,
|
||||
undefined,
|
||||
userId
|
||||
);
|
||||
});
|
||||
|
||||
// test case for duplication of "C (1)" but "C (2)" already exists, so it should create "C (3)"
|
||||
it('should create a new title if the quiz title already exists and ends with " (n)" but the incremented n also exists', async () => {
|
||||
const quizId = '60c72b2f9b1d8b3a4c8e4d3b';
|
||||
const userId = '12345';
|
||||
const newQuizId = ObjectId.createFromTime(Math.floor(Date.now() / 1000));
|
||||
const sourceQuiz = {
|
||||
title: 'Test Quiz (1)',
|
||||
content: 'This is a test quiz.',
|
||||
};
|
||||
|
||||
const createMock = jest.spyOn(quizzes, 'create').mockResolvedValue(newQuizId);
|
||||
|
||||
// mock the findOne method
|
||||
jest.spyOn(collection, 'findOne')
|
||||
.mockResolvedValueOnce(sourceQuiz) // source quiz exists
|
||||
.mockResolvedValueOnce({ title: 'Test Quiz (2)' }) // new name collision
|
||||
.mockResolvedValueOnce(null); // final new name is not found
|
||||
|
||||
const result = await quizzes.duplicate(quizId, userId);
|
||||
|
||||
expect(result).toBe(newQuizId);
|
||||
|
||||
// Ensure mocks were called correctly
|
||||
expect(createMock).toHaveBeenCalledWith(
|
||||
'Test Quiz (3)',
|
||||
sourceQuiz.content,
|
||||
undefined,
|
||||
userId
|
||||
);
|
||||
});
|
||||
|
||||
it('should throw an error if the quiz does not exist', async () => {
|
||||
const quizId = '60c72b2f9b1d8b3a4c8e4d3b';
|
||||
const userId = '12345';
|
||||
|
||||
// Mock the response from getContent
|
||||
jest.spyOn(quizzes, 'getContent').mockResolvedValue(null);
|
||||
|
||||
await expect(quizzes.duplicate(quizId, userId)).rejects.toThrow();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
@ -5,7 +5,8 @@ const { setupWebsocket } = require("../socket/socket");
|
|||
|
||||
process.env.NODE_ENV = "test";
|
||||
|
||||
const BACKEND_PORT = 4400;
|
||||
// pick a random port number for testing
|
||||
const BACKEND_PORT = Math.ceil(Math.random() * 1000 + 3000);
|
||||
const BACKEND_URL = "http://localhost";
|
||||
|
||||
const BACKEND_API = `${BACKEND_URL}:${BACKEND_PORT}`;
|
||||
|
|
|
|||
86
server/__tests__/users.test.js
Normal file
86
server/__tests__/users.test.js
Normal file
|
|
@ -0,0 +1,86 @@
|
|||
const Users = require('../models/users');
|
||||
const bcrypt = require('bcrypt');
|
||||
const Quizzes = require('../models/quiz');
|
||||
const Folders = require('../models/folders');
|
||||
const AppError = require('../middleware/AppError');
|
||||
const { ObjectId } = require('mongodb');
|
||||
|
||||
jest.mock('bcrypt');
|
||||
jest.mock('../middleware/AppError');
|
||||
jest.mock('../models/folders');
|
||||
|
||||
describe('Users', () => {
|
||||
let users;
|
||||
let db;
|
||||
|
||||
beforeEach(() => {
|
||||
jest.clearAllMocks(); // Clear any previous mock calls
|
||||
|
||||
// Mock the database connection
|
||||
db = {
|
||||
connect: jest.fn(),
|
||||
getConnection: jest.fn().mockReturnThis(), // Add getConnection method
|
||||
collection: jest.fn().mockReturnThis(),
|
||||
findOne: jest.fn(),
|
||||
insertOne: jest.fn().mockResolvedValue({ insertedId: new ObjectId() }), // Mock insertOne to return an ObjectId
|
||||
updateOne: jest.fn(),
|
||||
deleteOne: jest.fn(),
|
||||
};
|
||||
|
||||
const quizModel = new Quizzes(db);
|
||||
const foldersModel = new Folders(db, quizModel);
|
||||
|
||||
users = new Users(db, foldersModel);
|
||||
});
|
||||
|
||||
it('should register a new user', async () => {
|
||||
db.collection().findOne.mockResolvedValue(null); // No user found
|
||||
db.collection().insertOne.mockResolvedValue({ insertedId: new ObjectId() });
|
||||
bcrypt.hash.mockResolvedValue('hashedPassword');
|
||||
users.folders.create.mockResolvedValue(true);
|
||||
|
||||
const email = 'test@example.com';
|
||||
const password = 'password123';
|
||||
const result = await users.register(email, password);
|
||||
|
||||
expect(db.connect).toHaveBeenCalled();
|
||||
expect(db.collection().findOne).toHaveBeenCalledWith({ email });
|
||||
expect(bcrypt.hash).toHaveBeenCalledWith(password, 10);
|
||||
expect(db.collection().insertOne).toHaveBeenCalledWith({
|
||||
email,
|
||||
password: 'hashedPassword',
|
||||
created_at: expect.any(Date),
|
||||
});
|
||||
expect(users.folders.create).toHaveBeenCalledWith('Dossier par Défaut', expect.any(String));
|
||||
expect(result.insertedId).toBeDefined(); // Ensure result has insertedId
|
||||
});
|
||||
|
||||
// it('should update the user password', async () => {
|
||||
// db.collection().updateOne.mockResolvedValue({ modifiedCount: 1 });
|
||||
// bcrypt.hash.mockResolvedValue('hashedPassword');
|
||||
|
||||
// const email = 'test@example.com';
|
||||
// const newPassword = 'newPassword123';
|
||||
// const result = await users.updatePassword(email, newPassword);
|
||||
|
||||
// expect(db.connect).toHaveBeenCalled();
|
||||
// expect(db.collection().updateOne).toHaveBeenCalledWith(
|
||||
// { email },
|
||||
// { $set: { password: 'hashedPassword' } }
|
||||
// );
|
||||
// expect(result).toEqual(newPassword);
|
||||
// });
|
||||
|
||||
// it('should delete a user', async () => {
|
||||
// db.collection().deleteOne.mockResolvedValue({ deletedCount: 1 });
|
||||
|
||||
// const email = 'test@example.com';
|
||||
// const result = await users.delete(email);
|
||||
|
||||
// expect(db.connect).toHaveBeenCalled();
|
||||
// expect(db.collection().deleteOne).toHaveBeenCalledWith({ email });
|
||||
// expect(result).toBe(true);
|
||||
// });
|
||||
|
||||
// Add more tests as needed
|
||||
});
|
||||
|
|
@ -7,7 +7,35 @@ const dotenv = require('dotenv')
|
|||
const { setupWebsocket } = require("./socket/socket");
|
||||
const { Server } = require("socket.io");
|
||||
|
||||
//import routers
|
||||
// instantiate the db
|
||||
const db = require('./config/db.js');
|
||||
// instantiate the models
|
||||
const quiz = require('./models/quiz.js');
|
||||
const quizModel = new quiz(db);
|
||||
const folders = require('./models/folders.js');
|
||||
const foldersModel = new folders(db, quizModel);
|
||||
const users = require('./models/users.js');
|
||||
const userModel = new users(db, foldersModel);
|
||||
const images = require('./models/images.js');
|
||||
const imageModel = new images(db);
|
||||
|
||||
// instantiate the controllers
|
||||
const usersController = require('./controllers/users.js');
|
||||
const usersControllerInstance = new usersController(userModel);
|
||||
const foldersController = require('./controllers/folders.js');
|
||||
const foldersControllerInstance = new foldersController(foldersModel);
|
||||
const quizController = require('./controllers/quiz.js');
|
||||
const quizControllerInstance = new quizController(quizModel, foldersModel);
|
||||
const imagesController = require('./controllers/images.js');
|
||||
const imagesControllerInstance = new imagesController(imageModel);
|
||||
|
||||
// export the controllers
|
||||
module.exports.users = usersControllerInstance;
|
||||
module.exports.folders = foldersControllerInstance;
|
||||
module.exports.quizzes = quizControllerInstance;
|
||||
module.exports.images = imagesControllerInstance;
|
||||
|
||||
//import routers (instantiate controllers as side effect)
|
||||
const userRouter = require('./routers/users.js');
|
||||
const folderRouter = require('./routers/folders.js');
|
||||
const quizRouter = require('./routers/quiz.js');
|
||||
|
|
@ -15,7 +43,6 @@ const imagesRouter = require('./routers/images.js')
|
|||
|
||||
// Setup environement
|
||||
dotenv.config();
|
||||
const db = require('./config/db.js');
|
||||
const errorHandler = require("./middleware/errorHandler.js");
|
||||
|
||||
// Start app
|
||||
|
|
|
|||
|
|
@ -1,174 +1,170 @@
|
|||
//controller
|
||||
const model = require('../models/folders.js');
|
||||
|
||||
const AppError = require('../middleware/AppError.js');
|
||||
const { MISSING_REQUIRED_PARAMETER, NOT_IMPLEMENTED, FOLDER_NOT_FOUND, FOLDER_ALREADY_EXISTS, GETTING_FOLDER_ERROR, DELETE_FOLDER_ERROR, UPDATE_FOLDER_ERROR, MOVING_FOLDER_ERROR, DUPLICATE_FOLDER_ERROR, COPY_FOLDER_ERROR } = require('../constants/errorCodes');
|
||||
|
||||
// controllers must use arrow functions to bind 'this' to the class instance in order to access class properties as callbacks in Express
|
||||
class FoldersController {
|
||||
|
||||
constructor(foldersModel) {
|
||||
this.folders = foldersModel;
|
||||
}
|
||||
|
||||
/***
|
||||
* Basic queries
|
||||
*/
|
||||
async create(req, res, next) {
|
||||
create = async (req, res, next) => {
|
||||
try {
|
||||
const { title } = req.body;
|
||||
|
||||
|
||||
if (!title) {
|
||||
throw new AppError(MISSING_REQUIRED_PARAMETER);
|
||||
}
|
||||
|
||||
const result = await model.create(title, req.user.userId);
|
||||
|
||||
|
||||
const result = await this.folders.create(title, req.user.userId);
|
||||
|
||||
if (!result) {
|
||||
throw new AppError(FOLDER_ALREADY_EXISTS);
|
||||
}
|
||||
|
||||
|
||||
return res.status(200).json({
|
||||
message: 'Dossier créé avec succès.'
|
||||
});
|
||||
|
||||
}
|
||||
catch (error) {
|
||||
|
||||
} catch (error) {
|
||||
return next(error);
|
||||
}
|
||||
}
|
||||
|
||||
async getUserFolders(req, res, next) {
|
||||
|
||||
|
||||
getUserFolders = async (req, res, next) => {
|
||||
try {
|
||||
const folders = await model.getUserFolders(req.user.userId);
|
||||
|
||||
const folders = await this.folders.getUserFolders(req.user.userId);
|
||||
|
||||
if (!folders) {
|
||||
throw new AppError(FOLDER_NOT_FOUND);
|
||||
}
|
||||
|
||||
|
||||
return res.status(200).json({
|
||||
data: folders
|
||||
});
|
||||
|
||||
}
|
||||
catch (error) {
|
||||
|
||||
} catch (error) {
|
||||
return next(error);
|
||||
}
|
||||
}
|
||||
|
||||
async getFolderContent(req, res, next) {
|
||||
|
||||
getFolderContent = async (req, res, next) => {
|
||||
try {
|
||||
const { folderId } = req.params;
|
||||
|
||||
|
||||
if (!folderId) {
|
||||
throw new AppError(MISSING_REQUIRED_PARAMETER);
|
||||
}
|
||||
|
||||
|
||||
// Is this folder mine
|
||||
const owner = await model.getOwner(folderId);
|
||||
|
||||
const owner = await this.folders.getOwner(folderId);
|
||||
|
||||
if (owner != req.user.userId) {
|
||||
throw new AppError(FOLDER_NOT_FOUND);
|
||||
}
|
||||
|
||||
const content = await model.getContent(folderId);
|
||||
|
||||
|
||||
const content = await this.folders.getContent(folderId);
|
||||
|
||||
if (!content) {
|
||||
throw new AppError(GETTING_FOLDER_ERROR);
|
||||
}
|
||||
|
||||
|
||||
return res.status(200).json({
|
||||
data: content
|
||||
});
|
||||
|
||||
}
|
||||
catch (error) {
|
||||
|
||||
} catch (error) {
|
||||
return next(error);
|
||||
}
|
||||
}
|
||||
|
||||
async delete(req, res, next) {
|
||||
|
||||
delete = async (req, res, next) => {
|
||||
try {
|
||||
const { folderId } = req.params;
|
||||
|
||||
|
||||
if (!folderId) {
|
||||
throw new AppError(MISSING_REQUIRED_PARAMETER);
|
||||
}
|
||||
|
||||
|
||||
// Is this folder mine
|
||||
const owner = await model.getOwner(folderId);
|
||||
|
||||
const owner = await this.folders.getOwner(folderId);
|
||||
|
||||
if (owner != req.user.userId) {
|
||||
throw new AppError(FOLDER_NOT_FOUND);
|
||||
}
|
||||
|
||||
const result = await model.delete(folderId);
|
||||
|
||||
|
||||
const result = await this.folders.delete(folderId);
|
||||
|
||||
if (!result) {
|
||||
throw new AppError(DELETE_FOLDER_ERROR);
|
||||
}
|
||||
|
||||
|
||||
return res.status(200).json({
|
||||
message: 'Dossier supprimé avec succès.'
|
||||
});
|
||||
|
||||
}
|
||||
catch (error) {
|
||||
|
||||
} catch (error) {
|
||||
return next(error);
|
||||
}
|
||||
}
|
||||
|
||||
async rename(req, res, next) {
|
||||
|
||||
rename = async (req, res, next) => {
|
||||
try {
|
||||
const { folderId, newTitle } = req.body;
|
||||
|
||||
|
||||
if (!folderId || !newTitle) {
|
||||
throw new AppError(MISSING_REQUIRED_PARAMETER);
|
||||
}
|
||||
|
||||
|
||||
// Is this folder mine
|
||||
const owner = await model.getOwner(folderId);
|
||||
|
||||
const owner = await this.folders.getOwner(folderId);
|
||||
|
||||
if (owner != req.user.userId) {
|
||||
throw new AppError(FOLDER_NOT_FOUND);
|
||||
}
|
||||
|
||||
const result = await model.rename(folderId, newTitle);
|
||||
|
||||
|
||||
const result = await this.folders.rename(folderId, newTitle);
|
||||
|
||||
if (!result) {
|
||||
throw new AppError(UPDATE_FOLDER_ERROR);
|
||||
}
|
||||
|
||||
|
||||
return res.status(200).json({
|
||||
message: 'Dossier mis à jours avec succès.'
|
||||
});
|
||||
|
||||
}
|
||||
catch (error) {
|
||||
|
||||
} catch (error) {
|
||||
return next(error);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
async duplicate(req, res, next) {
|
||||
|
||||
duplicate = async (req, res, next) => {
|
||||
try {
|
||||
const { folderId, } = req.body;
|
||||
|
||||
if (!folderId ) {
|
||||
const { folderId } = req.body;
|
||||
|
||||
if (!folderId) {
|
||||
throw new AppError(MISSING_REQUIRED_PARAMETER);
|
||||
}
|
||||
|
||||
|
||||
// Is this folder mine
|
||||
const owner = await model.getOwner(folderId);
|
||||
|
||||
const owner = await this.folders.getOwner(folderId);
|
||||
|
||||
if (owner != req.user.userId) {
|
||||
throw new AppError(FOLDER_NOT_FOUND);
|
||||
}
|
||||
|
||||
const userId = req.user.userId;
|
||||
|
||||
const newFolderId = await model.duplicate(folderId, userId);
|
||||
|
||||
|
||||
const userId = req.user.userId;
|
||||
|
||||
const newFolderId = await this.folders.duplicate(folderId, userId);
|
||||
|
||||
if (!newFolderId) {
|
||||
throw new AppError(DUPLICATE_FOLDER_ERROR);
|
||||
}
|
||||
|
||||
|
||||
return res.status(200).json({
|
||||
message: 'Dossier dupliqué avec succès.',
|
||||
newFolderId: newFolderId
|
||||
|
|
@ -177,30 +173,30 @@ class FoldersController {
|
|||
return next(error);
|
||||
}
|
||||
}
|
||||
|
||||
async copy(req, res, next) {
|
||||
|
||||
copy = async (req, res, next) => {
|
||||
try {
|
||||
const { folderId, newTitle } = req.body;
|
||||
|
||||
|
||||
if (!folderId || !newTitle) {
|
||||
throw new AppError(MISSING_REQUIRED_PARAMETER);
|
||||
}
|
||||
|
||||
|
||||
// Is this folder mine
|
||||
const owner = await model.getOwner(folderId);
|
||||
|
||||
const owner = await this.folders.getOwner(folderId);
|
||||
|
||||
if (owner != req.user.userId) {
|
||||
throw new AppError(FOLDER_NOT_FOUND);
|
||||
}
|
||||
|
||||
|
||||
const userId = req.user.userId; // Assuming userId is obtained from authentication
|
||||
|
||||
const newFolderId = await model.copy(folderId, userId);
|
||||
|
||||
|
||||
const newFolderId = await this.folders.copy(folderId, userId);
|
||||
|
||||
if (!newFolderId) {
|
||||
throw new AppError(COPY_FOLDER_ERROR);
|
||||
}
|
||||
|
||||
|
||||
return res.status(200).json({
|
||||
message: 'Dossier copié avec succès.',
|
||||
newFolderId: newFolderId
|
||||
|
|
@ -210,27 +206,27 @@ class FoldersController {
|
|||
}
|
||||
}
|
||||
|
||||
async getFolderById(req, res, next) {
|
||||
getFolderById = async (req, res, next) => {
|
||||
try {
|
||||
const { folderId } = req.params;
|
||||
|
||||
|
||||
if (!folderId) {
|
||||
throw new AppError(MISSING_REQUIRED_PARAMETER);
|
||||
}
|
||||
|
||||
|
||||
// Is this folder mine
|
||||
const owner = await model.getOwner(folderId);
|
||||
|
||||
const owner = await this.folders.getOwner(folderId);
|
||||
|
||||
if (owner != req.user.userId) {
|
||||
throw new AppError(FOLDER_NOT_FOUND);
|
||||
}
|
||||
|
||||
const folder = await model.getFolderById(folderId);
|
||||
|
||||
|
||||
const folder = await this.folders.getFolderById(folderId);
|
||||
|
||||
if (!folder) {
|
||||
throw new AppError(FOLDER_NOT_FOUND);
|
||||
}
|
||||
|
||||
|
||||
return res.status(200).json({
|
||||
data: folder
|
||||
});
|
||||
|
|
@ -238,8 +234,8 @@ class FoldersController {
|
|||
return next(error);
|
||||
}
|
||||
}
|
||||
|
||||
async folderExists(req, res, next) {
|
||||
|
||||
folderExists = async (req, res, next) => {
|
||||
try {
|
||||
const { title } = req.body;
|
||||
|
||||
|
|
@ -247,10 +243,10 @@ class FoldersController {
|
|||
throw new AppError(MISSING_REQUIRED_PARAMETER);
|
||||
}
|
||||
|
||||
const userId = req.user.userId;
|
||||
const userId = req.user.userId;
|
||||
|
||||
// Vérifie si le dossier existe pour l'utilisateur donné
|
||||
const exists = await model.folderExists(title, userId);
|
||||
const exists = await this.folders.folderExists(title, userId);
|
||||
|
||||
return res.status(200).json({
|
||||
exists: exists
|
||||
|
|
@ -260,9 +256,8 @@ class FoldersController {
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
module.exports = new FoldersController;
|
||||
module.exports = FoldersController;
|
||||
|
|
|
|||
|
|
@ -1,56 +1,55 @@
|
|||
const model = require('../models/images.js');
|
||||
|
||||
const AppError = require('../middleware/AppError.js');
|
||||
const { MISSING_REQUIRED_PARAMETER, IMAGE_NOT_FOUND } = require('../constants/errorCodes');
|
||||
|
||||
class ImagesController {
|
||||
|
||||
async upload(req, res, next) {
|
||||
constructor(imagesModel) {
|
||||
this.images = imagesModel;
|
||||
}
|
||||
|
||||
upload = async (req, res, next) => {
|
||||
try {
|
||||
const file = req.file;
|
||||
|
||||
|
||||
if (!file) {
|
||||
throw new AppError(MISSING_REQUIRED_PARAMETER);
|
||||
}
|
||||
|
||||
const id = await model.upload(file, req.user.userId);
|
||||
|
||||
|
||||
const id = await this.images.upload(file, req.user.userId);
|
||||
|
||||
return res.status(200).json({
|
||||
id: id
|
||||
});
|
||||
}
|
||||
catch (error) {
|
||||
} catch (error) {
|
||||
return next(error);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
async get(req, res, next) {
|
||||
};
|
||||
|
||||
get = async (req, res, next) => {
|
||||
try {
|
||||
const { id } = req.params;
|
||||
|
||||
|
||||
if (!id) {
|
||||
throw new AppError(MISSING_REQUIRED_PARAMETER);
|
||||
}
|
||||
|
||||
const image = await model.get(id);
|
||||
|
||||
|
||||
const image = await this.images.get(id);
|
||||
|
||||
if (!image) {
|
||||
throw new AppError(IMAGE_NOT_FOUND)
|
||||
throw new AppError(IMAGE_NOT_FOUND);
|
||||
}
|
||||
|
||||
|
||||
// Set Headers for display in browser
|
||||
res.setHeader('Content-Type', image.mime_type);
|
||||
res.setHeader('Content-Disposition', 'inline; filename=' + image.file_name);
|
||||
res.setHeader('Accept-Ranges', 'bytes');
|
||||
res.setHeader('Cache-Control', 'no-cache, no-store, must-revalidate');
|
||||
return res.send(image.file_content);
|
||||
}
|
||||
catch (error) {
|
||||
} catch (error) {
|
||||
return next(error);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
module.exports = new ImagesController;
|
||||
module.exports = ImagesController;
|
||||
|
|
|
|||
|
|
@ -1,5 +1,3 @@
|
|||
const model = require('../models/quiz.js');
|
||||
const folderModel = require('../models/folders.js');
|
||||
const emailer = require('../config/email.js');
|
||||
|
||||
const AppError = require('../middleware/AppError.js');
|
||||
|
|
@ -7,184 +5,181 @@ const { MISSING_REQUIRED_PARAMETER, NOT_IMPLEMENTED, QUIZ_NOT_FOUND, FOLDER_NOT_
|
|||
|
||||
class QuizController {
|
||||
|
||||
async create(req, res, next) {
|
||||
constructor(quizModel, foldersModel) {
|
||||
this.folders = foldersModel;
|
||||
this.quizzes = quizModel;
|
||||
}
|
||||
|
||||
create = async (req, res, next) => {
|
||||
try {
|
||||
const { title, content, folderId } = req.body;
|
||||
|
||||
|
||||
if (!title || !content || !folderId) {
|
||||
throw new AppError(MISSING_REQUIRED_PARAMETER);
|
||||
}
|
||||
|
||||
|
||||
// Is this folder mine
|
||||
const owner = await folderModel.getOwner(folderId);
|
||||
|
||||
const owner = await this.folders.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.quizzes.create(title, content, folderId, req.user.userId);
|
||||
|
||||
if (!result) {
|
||||
throw new AppError(QUIZ_ALREADY_EXISTS);
|
||||
}
|
||||
|
||||
|
||||
return res.status(200).json({
|
||||
message: 'Quiz créé avec succès.'
|
||||
});
|
||||
|
||||
}
|
||||
catch (error) {
|
||||
|
||||
} catch (error) {
|
||||
return next(error);
|
||||
}
|
||||
}
|
||||
|
||||
async get(req, res, next) {
|
||||
};
|
||||
|
||||
get = async (req, res, next) => {
|
||||
try {
|
||||
const { quizId } = req.params;
|
||||
|
||||
|
||||
if (!quizId) {
|
||||
throw new AppError(MISSING_REQUIRED_PARAMETER);
|
||||
}
|
||||
|
||||
|
||||
const content = await model.getContent(quizId);
|
||||
|
||||
|
||||
const content = await this.quizzes.getContent(quizId);
|
||||
|
||||
if (!content) {
|
||||
throw new AppError(GETTING_QUIZ_ERROR);
|
||||
}
|
||||
|
||||
|
||||
// Is this quiz mine
|
||||
if (content.userId != req.user.userId) {
|
||||
throw new AppError(QUIZ_NOT_FOUND);
|
||||
}
|
||||
|
||||
|
||||
return res.status(200).json({
|
||||
data: content
|
||||
});
|
||||
|
||||
}
|
||||
catch (error) {
|
||||
|
||||
} catch (error) {
|
||||
return next(error);
|
||||
}
|
||||
}
|
||||
|
||||
async delete(req, res, next) {
|
||||
};
|
||||
|
||||
delete = async (req, res, next) => {
|
||||
try {
|
||||
const { quizId } = req.params;
|
||||
|
||||
|
||||
if (!quizId) {
|
||||
throw new AppError(MISSING_REQUIRED_PARAMETER);
|
||||
}
|
||||
|
||||
|
||||
// Is this quiz mine
|
||||
const owner = await model.getOwner(quizId);
|
||||
|
||||
const owner = await this.quizzes.getOwner(quizId);
|
||||
|
||||
if (owner != req.user.userId) {
|
||||
throw new AppError(QUIZ_NOT_FOUND);
|
||||
}
|
||||
|
||||
const result = await model.delete(quizId);
|
||||
|
||||
|
||||
const result = await this.quizzes.delete(quizId);
|
||||
|
||||
if (!result) {
|
||||
throw new AppError(DELETE_QUIZ_ERROR);
|
||||
}
|
||||
|
||||
|
||||
return res.status(200).json({
|
||||
message: 'Quiz supprimé avec succès.'
|
||||
});
|
||||
|
||||
}
|
||||
catch (error) {
|
||||
|
||||
} catch (error) {
|
||||
return next(error);
|
||||
}
|
||||
}
|
||||
|
||||
async update(req, res, next) {
|
||||
};
|
||||
|
||||
update = async (req, res, next) => {
|
||||
try {
|
||||
const { quizId, newTitle, newContent } = req.body;
|
||||
|
||||
|
||||
if (!newTitle || !newContent || !quizId) {
|
||||
throw new AppError(MISSING_REQUIRED_PARAMETER);
|
||||
}
|
||||
|
||||
|
||||
// Is this quiz mine
|
||||
const owner = await model.getOwner(quizId);
|
||||
|
||||
const owner = await this.quizzes.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.quizzes.update(quizId, newTitle, newContent);
|
||||
|
||||
if (!result) {
|
||||
throw new AppError(UPDATE_QUIZ_ERROR);
|
||||
}
|
||||
|
||||
|
||||
return res.status(200).json({
|
||||
message: 'Quiz mis à jours avec succès.'
|
||||
});
|
||||
|
||||
}
|
||||
catch (error) {
|
||||
|
||||
} catch (error) {
|
||||
return next(error);
|
||||
}
|
||||
}
|
||||
|
||||
async move(req, res, next) {
|
||||
};
|
||||
|
||||
move = async (req, res, next) => {
|
||||
try {
|
||||
const { quizId, newFolderId } = req.body;
|
||||
|
||||
|
||||
if (!quizId || !newFolderId) {
|
||||
throw new AppError(MISSING_REQUIRED_PARAMETER);
|
||||
}
|
||||
|
||||
|
||||
// Is this quiz mine
|
||||
const quizOwner = await model.getOwner(quizId);
|
||||
|
||||
const quizOwner = await this.quizzes.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.folders.getOwner(newFolderId);
|
||||
|
||||
if (folderOwner != req.user.userId) {
|
||||
throw new AppError(FOLDER_NOT_FOUND);
|
||||
}
|
||||
|
||||
const result = await model.move(quizId, newFolderId);
|
||||
|
||||
|
||||
const result = await this.quizzes.move(quizId, newFolderId);
|
||||
|
||||
if (!result) {
|
||||
throw new AppError(MOVING_QUIZ_ERROR);
|
||||
}
|
||||
|
||||
|
||||
return res.status(200).json({
|
||||
message: 'Utilisateur déplacé avec succès.'
|
||||
});
|
||||
|
||||
}
|
||||
catch (error) {
|
||||
|
||||
} catch (error) {
|
||||
return next(error);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
async copy(req, res, next) {
|
||||
copy = async (req, res, next) => {
|
||||
const { quizId, newTitle, folderId } = req.body;
|
||||
|
||||
|
||||
if (!quizId || !newTitle || !folderId) {
|
||||
throw new AppError(MISSING_REQUIRED_PARAMETER);
|
||||
}
|
||||
|
||||
|
||||
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) });
|
||||
// const quiztoduplicate = await conn.collection('quiz').findOne({ _id: ObjectId.createFromHexString(quizId) });
|
||||
// if (!quiztoduplicate) {
|
||||
// throw new Error("Quiz non trouvé");
|
||||
// }
|
||||
|
|
@ -192,121 +187,119 @@ class QuizController {
|
|||
// //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) });
|
||||
// await conn.collection('quiz').insertOne({ ...quiztoduplicate, userId: ObjectId.createFromHexString(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) {
|
||||
};
|
||||
|
||||
deleteQuizzesByFolderId = async (req, res, next) => {
|
||||
try {
|
||||
const { folderId } = req.body;
|
||||
|
||||
|
||||
if (!folderId) {
|
||||
throw new AppError(MISSING_REQUIRED_PARAMETER);
|
||||
}
|
||||
|
||||
|
||||
// Call the method from the Quiz model to delete quizzes by folder ID
|
||||
await Quiz.deleteQuizzesByFolderId(folderId);
|
||||
|
||||
|
||||
return res.status(200).json({
|
||||
message: 'Quizzes deleted successfully.'
|
||||
});
|
||||
} catch (error) {
|
||||
return next(error);
|
||||
}
|
||||
}
|
||||
|
||||
async duplicate(req, res, next) {
|
||||
const { quizId } = req.body;
|
||||
|
||||
};
|
||||
|
||||
duplicate = async (req, res, next) => {
|
||||
const { quizId } = req.body;
|
||||
|
||||
try {
|
||||
const newQuizId = await model.duplicate(quizId,req.user.userId);
|
||||
const newQuizId = await this.quizzes.duplicate(quizId, req.user.userId);
|
||||
res.status(200).json({ success: true, newQuizId });
|
||||
} catch (error) {
|
||||
return next(error);
|
||||
}
|
||||
}
|
||||
|
||||
async quizExists(title, userId) {
|
||||
};
|
||||
|
||||
quizExists = async (title, userId) => {
|
||||
try {
|
||||
const existingFile = await model.quizExists(title, userId);
|
||||
const existingFile = await this.quizzes.quizExists(title, userId);
|
||||
return existingFile !== null;
|
||||
} catch (error) {
|
||||
throw new AppError(GETTING_QUIZ_ERROR);
|
||||
}
|
||||
}
|
||||
|
||||
async Share(req, res, next) {
|
||||
};
|
||||
|
||||
share = async (req, res, next) => {
|
||||
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);
|
||||
|
||||
return res.status(200).json({
|
||||
message: 'Quiz partagé avec succès.'
|
||||
});
|
||||
|
||||
}
|
||||
catch (error) {
|
||||
} catch (error) {
|
||||
return next(error);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
async getShare(req, res, next) {
|
||||
getShare = async (req, res, next) => {
|
||||
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.quizzes.getContent(quizId);
|
||||
|
||||
if (!content) {
|
||||
throw new AppError(GETTING_QUIZ_ERROR);
|
||||
}
|
||||
|
||||
|
||||
return res.status(200).json({
|
||||
data: content.title
|
||||
});
|
||||
|
||||
}
|
||||
catch (error) {
|
||||
} catch (error) {
|
||||
return next(error);
|
||||
}
|
||||
}
|
||||
|
||||
async receiveShare(req, res, next) {
|
||||
};
|
||||
|
||||
receiveShare = async (req, res, next) => {
|
||||
try {
|
||||
const { quizId, folderId } = req.body;
|
||||
|
||||
if (!quizId || !folderId) {
|
||||
throw new AppError(MISSING_REQUIRED_PARAMETER);
|
||||
}
|
||||
|
||||
const folderOwner = await folderModel.getOwner(folderId);
|
||||
|
||||
const folderOwner = await this.folders.getOwner(folderId);
|
||||
if (folderOwner != req.user.userId) {
|
||||
throw new AppError(FOLDER_NOT_FOUND);
|
||||
}
|
||||
|
||||
const content = await model.getContent(quizId);
|
||||
const content = await this.quizzes.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.quizzes.create(content.title, content.content, folderId, req.user.userId);
|
||||
if (!result) {
|
||||
throw new AppError(QUIZ_ALREADY_EXISTS);
|
||||
}
|
||||
|
|
@ -314,13 +307,11 @@ class QuizController {
|
|||
return res.status(200).json({
|
||||
message: 'Quiz partagé reçu.'
|
||||
});
|
||||
}
|
||||
catch (error) {
|
||||
} catch (error) {
|
||||
return next(error);
|
||||
}
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
module.exports = new QuizController;
|
||||
module.exports = QuizController;
|
||||
|
|
|
|||
|
|
@ -1,35 +1,40 @@
|
|||
const emailer = require('../config/email.js');
|
||||
const model = require('../models/users.js');
|
||||
const jwt = require('../middleware/jwtToken.js');
|
||||
|
||||
const AppError = require('../middleware/AppError.js');
|
||||
const { MISSING_REQUIRED_PARAMETER, LOGIN_CREDENTIALS_ERROR, GENERATE_PASSWORD_ERROR, UPDATE_PASSWORD_ERROR, DELETE_USER_ERROR } = require('../constants/errorCodes');
|
||||
|
||||
// controllers must use arrow functions to bind 'this' to the class instance in order to access class properties as callbacks in Express
|
||||
class UsersController {
|
||||
|
||||
async register(req, res, next) {
|
||||
constructor(userModel) {
|
||||
this.users = userModel;
|
||||
}
|
||||
|
||||
register = async (req, res, next) => {
|
||||
try {
|
||||
const { email, password } = req.body;
|
||||
|
||||
|
||||
if (!email || !password) {
|
||||
throw new AppError(MISSING_REQUIRED_PARAMETER);
|
||||
}
|
||||
|
||||
await model.register(email, password);
|
||||
|
||||
emailer.registerConfirmation(email)
|
||||
|
||||
|
||||
if (!this.users) {
|
||||
throw new AppError('Users model not found');
|
||||
}
|
||||
await this.users.register(email, password);
|
||||
|
||||
emailer.registerConfirmation(email);
|
||||
|
||||
return res.status(200).json({
|
||||
message: 'Utilisateur créé avec succès.'
|
||||
});
|
||||
|
||||
}
|
||||
catch (error) {
|
||||
|
||||
} catch (error) {
|
||||
return next(error);
|
||||
}
|
||||
}
|
||||
|
||||
async login(req, res, next) {
|
||||
login = async (req, res, next) => {
|
||||
try {
|
||||
const { email, password } = req.body;
|
||||
|
||||
|
|
@ -37,7 +42,11 @@ class UsersController {
|
|||
throw new AppError(MISSING_REQUIRED_PARAMETER);
|
||||
}
|
||||
|
||||
const user = await model.login(email, password);
|
||||
if (!this) {
|
||||
throw new AppError('UsersController not initialized');
|
||||
}
|
||||
|
||||
const user = await this.users.login(email, password);
|
||||
|
||||
if (!user) {
|
||||
throw new AppError(LOGIN_CREDENTIALS_ERROR);
|
||||
|
|
@ -45,102 +54,93 @@ class UsersController {
|
|||
|
||||
const token = jwt.create(user.email, user._id);
|
||||
|
||||
return res.status(200).json({
|
||||
token: token,
|
||||
id: user.email
|
||||
});
|
||||
|
||||
}
|
||||
catch (error) {
|
||||
return next(error);
|
||||
return res.status(200).json({ token });
|
||||
} catch (error) {
|
||||
next(error);
|
||||
}
|
||||
}
|
||||
|
||||
async resetPassword(req, res, next) {
|
||||
resetPassword = async (req, res, next) => {
|
||||
try {
|
||||
const { email } = req.body;
|
||||
|
||||
|
||||
if (!email) {
|
||||
throw new AppError(MISSING_REQUIRED_PARAMETER);
|
||||
}
|
||||
|
||||
const newPassword = await model.resetPassword(email);
|
||||
|
||||
|
||||
const newPassword = await this.users.resetPassword(email);
|
||||
|
||||
if (!newPassword) {
|
||||
throw new AppError(GENERATE_PASSWORD_ERROR);
|
||||
}
|
||||
|
||||
|
||||
emailer.newPasswordConfirmation(email, newPassword);
|
||||
|
||||
|
||||
return res.status(200).json({
|
||||
message: 'Nouveau mot de passe envoyé par courriel.'
|
||||
});
|
||||
}
|
||||
catch (error) {
|
||||
} catch (error) {
|
||||
return next(error);
|
||||
}
|
||||
}
|
||||
|
||||
async changePassword(req, res, next) {
|
||||
|
||||
changePassword = async (req, res, next) => {
|
||||
try {
|
||||
const { email, oldPassword, newPassword } = req.body;
|
||||
|
||||
|
||||
if (!email || !oldPassword || !newPassword) {
|
||||
throw new AppError(MISSING_REQUIRED_PARAMETER);
|
||||
}
|
||||
|
||||
|
||||
// verify creds first
|
||||
const user = await model.login(email, oldPassword);
|
||||
|
||||
const user = await this.users.login(email, oldPassword);
|
||||
|
||||
if (!user) {
|
||||
throw new AppError(LOGIN_CREDENTIALS_ERROR);
|
||||
}
|
||||
|
||||
const password = await model.changePassword(email, newPassword)
|
||||
|
||||
|
||||
const password = await this.users.changePassword(email, newPassword);
|
||||
|
||||
if (!password) {
|
||||
throw new AppError(UPDATE_PASSWORD_ERROR);
|
||||
}
|
||||
|
||||
|
||||
return res.status(200).json({
|
||||
message: 'Mot de passe changé avec succès.'
|
||||
});
|
||||
}
|
||||
catch (error) {
|
||||
} catch (error) {
|
||||
return next(error);
|
||||
}
|
||||
}
|
||||
|
||||
async delete(req, res, next) {
|
||||
|
||||
delete = async (req, res, next) => {
|
||||
try {
|
||||
const { email, password } = req.body;
|
||||
|
||||
|
||||
if (!email || !password) {
|
||||
throw new AppError(MISSING_REQUIRED_PARAMETER);
|
||||
}
|
||||
|
||||
|
||||
// verify creds first
|
||||
const user = await model.login(email, password);
|
||||
|
||||
const user = await this.users.login(email, password);
|
||||
|
||||
if (!user) {
|
||||
throw new AppError(LOGIN_CREDENTIALS_ERROR);
|
||||
}
|
||||
|
||||
const result = await model.delete(email)
|
||||
|
||||
|
||||
const result = await this.users.delete(email);
|
||||
|
||||
if (!result) {
|
||||
throw new AppError(DELETE_USER_ERROR)
|
||||
throw new AppError(DELETE_USER_ERROR);
|
||||
}
|
||||
|
||||
|
||||
return res.status(200).json({
|
||||
message: 'Utilisateur supprimé avec succès'
|
||||
});
|
||||
}
|
||||
catch (error) {
|
||||
} catch (error) {
|
||||
return next(error);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
module.exports = new UsersController;
|
||||
module.exports = UsersController;
|
||||
|
|
|
|||
|
|
@ -1,19 +1,31 @@
|
|||
//model
|
||||
const db = require('../config/db.js')
|
||||
const { ObjectId } = require('mongodb');
|
||||
const Quiz = require('./quiz.js');
|
||||
const ObjectId = require('mongodb').ObjectId;
|
||||
const { generateUniqueTitle } = require('./utils');
|
||||
|
||||
class Folders {
|
||||
constructor(db, quizModel) {
|
||||
this.db = db;
|
||||
this.quizModel = quizModel;
|
||||
}
|
||||
|
||||
async create(title, userId) {
|
||||
await db.connect()
|
||||
const conn = db.getConnection();
|
||||
|
||||
console.log("LOG: create", title, userId);
|
||||
|
||||
if (!title || !userId) {
|
||||
throw new Error('Missing required parameter(s)');
|
||||
}
|
||||
|
||||
await this.db.connect()
|
||||
const conn = this.db.getConnection();
|
||||
|
||||
const foldersCollection = conn.collection('folders');
|
||||
|
||||
const existingFolder = await foldersCollection.findOne({ title: title, userId: userId });
|
||||
|
||||
if (existingFolder) return new Error('Folder already exists');
|
||||
if (existingFolder) {
|
||||
throw new Error('Folder already exists');
|
||||
}
|
||||
|
||||
const newFolder = {
|
||||
userId: userId,
|
||||
|
|
@ -27,8 +39,8 @@ class Folders {
|
|||
}
|
||||
|
||||
async getUserFolders(userId) {
|
||||
await db.connect()
|
||||
const conn = db.getConnection();
|
||||
await this.db.connect()
|
||||
const conn = this.db.getConnection();
|
||||
|
||||
const foldersCollection = conn.collection('folders');
|
||||
|
||||
|
|
@ -38,19 +50,20 @@ class Folders {
|
|||
}
|
||||
|
||||
async getOwner(folderId) {
|
||||
await db.connect()
|
||||
const conn = db.getConnection();
|
||||
await this.db.connect()
|
||||
const conn = this.db.getConnection();
|
||||
|
||||
const foldersCollection = conn.collection('folders');
|
||||
|
||||
const folder = await foldersCollection.findOne({ _id: new ObjectId(folderId) });
|
||||
const folder = await foldersCollection.findOne({ _id: ObjectId.createFromHexString(folderId) });
|
||||
|
||||
return folder.userId;
|
||||
}
|
||||
|
||||
// finds all quizzes in a folder
|
||||
async getContent(folderId) {
|
||||
await db.connect()
|
||||
const conn = db.getConnection();
|
||||
await this.db.connect()
|
||||
const conn = this.db.getConnection();
|
||||
|
||||
const filesCollection = conn.collection('files');
|
||||
|
||||
|
|
@ -60,26 +73,26 @@ class Folders {
|
|||
}
|
||||
|
||||
async delete(folderId) {
|
||||
await db.connect()
|
||||
const conn = db.getConnection();
|
||||
await this.db.connect()
|
||||
const conn = this.db.getConnection();
|
||||
|
||||
const foldersCollection = conn.collection('folders');
|
||||
|
||||
const folderResult = await foldersCollection.deleteOne({ _id: new ObjectId(folderId) });
|
||||
const folderResult = await foldersCollection.deleteOne({ _id: ObjectId.createFromHexString(folderId) });
|
||||
|
||||
if (folderResult.deletedCount != 1) return false;
|
||||
await Quiz.deleteQuizzesByFolderId(folderId);
|
||||
await this.quizModel.deleteQuizzesByFolderId(folderId);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
async rename(folderId, newTitle) {
|
||||
await db.connect()
|
||||
const conn = db.getConnection();
|
||||
await this.db.connect()
|
||||
const conn = this.db.getConnection();
|
||||
|
||||
const foldersCollection = conn.collection('folders');
|
||||
|
||||
const result = await foldersCollection.updateOne({ _id: new ObjectId(folderId) }, { $set: { title: newTitle } })
|
||||
const result = await foldersCollection.updateOne({ _id: ObjectId.createFromHexString(folderId) }, { $set: { title: newTitle } })
|
||||
|
||||
if (result.modifiedCount != 1) return false;
|
||||
|
||||
|
|
@ -87,69 +100,77 @@ class Folders {
|
|||
}
|
||||
|
||||
async duplicate(folderId, userId) {
|
||||
console.log("LOG: duplicate", folderId, userId);
|
||||
const conn = this.db.getConnection();
|
||||
const foldersCollection = conn.collection('folders');
|
||||
|
||||
const sourceFolder = await this.getFolderWithContent(folderId);
|
||||
|
||||
// Check if the new title already exists
|
||||
let newFolderTitle = sourceFolder.title + "-copie";
|
||||
let counter = 1;
|
||||
|
||||
while (await this.folderExists(newFolderTitle, userId)) {
|
||||
newFolderTitle = `${sourceFolder.title}-copie(${counter})`;
|
||||
counter++;
|
||||
const sourceFolder = await foldersCollection.findOne({ _id: ObjectId.createFromHexString(folderId), userId: userId });
|
||||
if (!sourceFolder) {
|
||||
throw new Error(`Folder ${folderId} not found`);
|
||||
}
|
||||
|
||||
|
||||
|
||||
const theUserId = userId;
|
||||
// Use the utility function to generate a unique title
|
||||
const newFolderTitle = await generateUniqueTitle(sourceFolder.title, async (title) => {
|
||||
console.log(`generateUniqueTitle(${title}): userId`, theUserId);
|
||||
return await foldersCollection.findOne({ title: title, userId: theUserId });
|
||||
});
|
||||
|
||||
const newFolderId = await this.create(newFolderTitle, userId);
|
||||
|
||||
if (!newFolderId) {
|
||||
throw new Error('Failed to create a duplicate folder.');
|
||||
throw new Error('Failed to create duplicate folder');
|
||||
}
|
||||
|
||||
for (const quiz of sourceFolder.content) {
|
||||
const { title, content } = quiz;
|
||||
//console.log(title);
|
||||
//console.log(content);
|
||||
await Quiz.create(title, content, newFolderId.toString(), userId);
|
||||
// copy the quizzes from source folder to destination folder
|
||||
const content = await this.getContent(folderId);
|
||||
console.log("folders.duplicate: found content", content);
|
||||
for (const quiz of content) {
|
||||
console.log("folders.duplicate: creating quiz (copy)", quiz);
|
||||
const result = await this.quizModel.create(quiz.title, quiz.content, newFolderId.toString(), userId);
|
||||
if (!result) {
|
||||
throw new Error('Failed to create duplicate quiz');
|
||||
}
|
||||
}
|
||||
|
||||
return newFolderId;
|
||||
|
||||
}
|
||||
|
||||
async folderExists(title, userId) {
|
||||
await db.connect();
|
||||
const conn = db.getConnection();
|
||||
console.log("LOG: folderExists", title, userId);
|
||||
await this.db.connect();
|
||||
const conn = this.db.getConnection();
|
||||
|
||||
const foldersCollection = conn.collection('folders');
|
||||
const existingFolder = await foldersCollection.findOne({ title: title, userId: userId });
|
||||
|
||||
return existingFolder !== null;
|
||||
return !!existingFolder;
|
||||
}
|
||||
|
||||
|
||||
async copy(folderId, userId) {
|
||||
|
||||
|
||||
const sourceFolder = await this.getFolderWithContent(folderId);
|
||||
const newFolderId = await this.create(sourceFolder.title, userId);
|
||||
if (!newFolderId) {
|
||||
throw new Error('Failed to create a new folder.');
|
||||
}
|
||||
for (const quiz of sourceFolder.content) {
|
||||
await this.createQuiz(quiz.title, quiz.content, newFolderId, userId);
|
||||
await this.quizModel.create(quiz.title, quiz.content, newFolderId, userId);
|
||||
}
|
||||
|
||||
return newFolderId;
|
||||
|
||||
}
|
||||
|
||||
async getFolderById(folderId) {
|
||||
await db.connect();
|
||||
const conn = db.getConnection();
|
||||
await this.db.connect();
|
||||
const conn = this.db.getConnection();
|
||||
|
||||
const foldersCollection = conn.collection('folders');
|
||||
|
||||
const folder = await foldersCollection.findOne({ _id: new ObjectId(folderId) });
|
||||
const folder = await foldersCollection.findOne({ _id: ObjectId.createFromHexString(folderId) });
|
||||
|
||||
if (!folder) return new Error(`Folder ${folderId} not found`);
|
||||
|
||||
return folder;
|
||||
}
|
||||
|
|
@ -171,4 +192,4 @@ class Folders {
|
|||
|
||||
}
|
||||
|
||||
module.exports = new Folders;
|
||||
module.exports = Folders;
|
||||
|
|
|
|||
|
|
@ -1,8 +1,12 @@
|
|||
const db = require('../config/db.js')
|
||||
//const db = require('../config/db.js')
|
||||
const { ObjectId } = require('mongodb');
|
||||
|
||||
class Images {
|
||||
|
||||
constructor(db) {
|
||||
this.db = db;
|
||||
}
|
||||
|
||||
async upload(file, userId) {
|
||||
await db.connect()
|
||||
const conn = db.getConnection();
|
||||
|
|
@ -28,7 +32,7 @@ class Images {
|
|||
|
||||
const imagesCollection = conn.collection('images');
|
||||
|
||||
const result = await imagesCollection.findOne({ _id: new ObjectId(id) });
|
||||
const result = await imagesCollection.findOne({ _id: ObjectId.createFromHexString(id) });
|
||||
|
||||
if (!result) return null;
|
||||
|
||||
|
|
@ -41,4 +45,4 @@ class Images {
|
|||
|
||||
}
|
||||
|
||||
module.exports = new Images;
|
||||
module.exports = Images;
|
||||
|
|
|
|||
|
|
@ -1,17 +1,25 @@
|
|||
const db = require('../config/db.js')
|
||||
const { ObjectId } = require('mongodb');
|
||||
const { generateUniqueTitle } = require('./utils');
|
||||
|
||||
class Quiz {
|
||||
|
||||
constructor(db) {
|
||||
// console.log("Quiz constructor: db", db)
|
||||
this.db = db;
|
||||
}
|
||||
|
||||
async create(title, content, folderId, userId) {
|
||||
await db.connect()
|
||||
const conn = db.getConnection();
|
||||
console.log(`quizzes: create title: ${title}, folderId: ${folderId}, userId: ${userId}`);
|
||||
await this.db.connect()
|
||||
const conn = this.db.getConnection();
|
||||
|
||||
const quizCollection = conn.collection('files');
|
||||
|
||||
const existingQuiz = await quizCollection.findOne({ title: title, folderId: folderId, userId: userId })
|
||||
|
||||
if (existingQuiz) return null;
|
||||
if (existingQuiz) {
|
||||
throw new Error(`Quiz already exists with title: ${title}, folderId: ${folderId}, userId: ${userId}`);
|
||||
}
|
||||
|
||||
const newQuiz = {
|
||||
folderId: folderId,
|
||||
|
|
@ -23,74 +31,86 @@ class Quiz {
|
|||
}
|
||||
|
||||
const result = await quizCollection.insertOne(newQuiz);
|
||||
console.log("quizzes: create insertOne result", result);
|
||||
|
||||
return result.insertedId;
|
||||
}
|
||||
|
||||
async getOwner(quizId) {
|
||||
await db.connect()
|
||||
const conn = db.getConnection();
|
||||
await this.db.connect()
|
||||
const conn = this.db.getConnection();
|
||||
|
||||
const quizCollection = conn.collection('files');
|
||||
|
||||
const quiz = await quizCollection.findOne({ _id: new ObjectId(quizId) });
|
||||
const quiz = await quizCollection.findOne({ _id: ObjectId.createFromHexString(quizId) });
|
||||
|
||||
return quiz.userId;
|
||||
}
|
||||
|
||||
async getContent(quizId) {
|
||||
await db.connect()
|
||||
const conn = db.getConnection();
|
||||
await this.db.connect()
|
||||
const conn = this.db.getConnection();
|
||||
|
||||
const quizCollection = conn.collection('files');
|
||||
|
||||
const quiz = await quizCollection.findOne({ _id: new ObjectId(quizId) });
|
||||
const quiz = await quizCollection.findOne({ _id: ObjectId.createFromHexString(quizId) });
|
||||
|
||||
return quiz;
|
||||
}
|
||||
|
||||
async delete(quizId) {
|
||||
await db.connect()
|
||||
const conn = db.getConnection();
|
||||
await this.db.connect()
|
||||
const conn = this.db.getConnection();
|
||||
|
||||
const quizCollection = conn.collection('files');
|
||||
|
||||
const result = await quizCollection.deleteOne({ _id: new ObjectId(quizId) });
|
||||
const result = await quizCollection.deleteOne({ _id: ObjectId.createFromHexString(quizId) });
|
||||
|
||||
if (result.deletedCount != 1) return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
async deleteQuizzesByFolderId(folderId) {
|
||||
await db.connect();
|
||||
const conn = db.getConnection();
|
||||
await this.db.connect();
|
||||
const conn = this.db.getConnection();
|
||||
|
||||
const quizzesCollection = conn.collection('files');
|
||||
|
||||
// Delete all quizzes with the specified folderId
|
||||
await quizzesCollection.deleteMany({ folderId: folderId });
|
||||
const result = await quizzesCollection.deleteMany({ folderId: folderId });
|
||||
return result.deletedCount > 0;
|
||||
}
|
||||
|
||||
async update(quizId, newTitle, newContent) {
|
||||
await db.connect()
|
||||
const conn = db.getConnection();
|
||||
await this.db.connect()
|
||||
const conn = this.db.getConnection();
|
||||
|
||||
const quizCollection = conn.collection('files');
|
||||
|
||||
const result = await quizCollection.updateOne({ _id: new ObjectId(quizId) }, { $set: { title: newTitle, content: newContent } });
|
||||
//Ne fonctionne pas si rien n'est chngé dans le quiz
|
||||
//if (result.modifiedCount != 1) return false;
|
||||
const result = await quizCollection.updateOne(
|
||||
{ _id: ObjectId.createFromHexString(quizId) },
|
||||
{
|
||||
$set: {
|
||||
title: newTitle,
|
||||
content: newContent,
|
||||
updated_at: new Date()
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
return true
|
||||
return result.modifiedCount === 1;
|
||||
}
|
||||
|
||||
async move(quizId, newFolderId) {
|
||||
await db.connect()
|
||||
const conn = db.getConnection();
|
||||
await this.db.connect()
|
||||
const conn = this.db.getConnection();
|
||||
|
||||
const quizCollection = conn.collection('files');
|
||||
|
||||
const result = await quizCollection.updateOne({ _id: new ObjectId(quizId) }, { $set: { folderId: newFolderId } });
|
||||
const result = await quizCollection.updateOne(
|
||||
{ _id: ObjectId.createFromHexString(quizId) },
|
||||
{ $set: { folderId: newFolderId } }
|
||||
);
|
||||
|
||||
if (result.modifiedCount != 1) return false;
|
||||
|
||||
|
|
@ -98,29 +118,31 @@ class Quiz {
|
|||
}
|
||||
|
||||
async duplicate(quizId, userId) {
|
||||
|
||||
const sourceQuiz = await this.getContent(quizId);
|
||||
|
||||
let newQuizTitle = `${sourceQuiz.title}-copy`;
|
||||
let counter = 1;
|
||||
while (await this.quizExists(newQuizTitle, userId)) {
|
||||
newQuizTitle = `${sourceQuiz.title}-copy(${counter})`;
|
||||
counter++;
|
||||
const conn = this.db.getConnection();
|
||||
const quizCollection = conn.collection('files');
|
||||
|
||||
const sourceQuiz = await quizCollection.findOne({ _id: ObjectId.createFromHexString(quizId), userId: userId });
|
||||
if (!sourceQuiz) {
|
||||
throw new Error('Quiz not found for quizId: ' + quizId);
|
||||
}
|
||||
//console.log(newQuizTitle);
|
||||
const newQuizId = await this.create(newQuizTitle, sourceQuiz.content,sourceQuiz.folderId, userId);
|
||||
|
||||
// Use the utility function to generate a unique title
|
||||
const newQuizTitle = await generateUniqueTitle(sourceQuiz.title, async (title) => {
|
||||
return await quizCollection.findOne({ title: title, folderId: sourceQuiz.folderId, userId: userId });
|
||||
});
|
||||
|
||||
const newQuizId = await this.create(newQuizTitle, sourceQuiz.content, sourceQuiz.folderId, userId);
|
||||
|
||||
if (!newQuizId) {
|
||||
throw new Error('Failed to create a duplicate quiz.');
|
||||
throw new Error('Failed to create duplicate quiz');
|
||||
}
|
||||
|
||||
return newQuizId;
|
||||
|
||||
}
|
||||
|
||||
async quizExists(title, userId) {
|
||||
await db.connect();
|
||||
const conn = db.getConnection();
|
||||
await this.db.connect();
|
||||
const conn = this.db.getConnection();
|
||||
|
||||
const filesCollection = conn.collection('files');
|
||||
const existingFolder = await filesCollection.findOne({ title: title, userId: userId });
|
||||
|
|
@ -130,4 +152,4 @@ class Quiz {
|
|||
|
||||
}
|
||||
|
||||
module.exports = new Quiz;
|
||||
module.exports = Quiz;
|
||||
|
|
|
|||
|
|
@ -1,11 +1,14 @@
|
|||
//user
|
||||
const db = require('../config/db.js');
|
||||
const bcrypt = require('bcrypt');
|
||||
const AppError = require('../middleware/AppError.js');
|
||||
const { USER_ALREADY_EXISTS } = require('../constants/errorCodes');
|
||||
const Folders = require('../models/folders.js');
|
||||
|
||||
class Users {
|
||||
constructor(db, foldersModel) {
|
||||
// console.log("Users constructor: db", db)
|
||||
this.db = db;
|
||||
this.folders = foldersModel;
|
||||
}
|
||||
|
||||
async hashPassword(password) {
|
||||
return await bcrypt.hash(password, 10)
|
||||
|
|
@ -20,8 +23,8 @@ class Users {
|
|||
}
|
||||
|
||||
async register(email, password) {
|
||||
await db.connect()
|
||||
const conn = db.getConnection();
|
||||
await this.db.connect()
|
||||
const conn = this.db.getConnection();
|
||||
|
||||
const userCollection = conn.collection('users');
|
||||
|
||||
|
|
@ -37,18 +40,19 @@ class Users {
|
|||
created_at: new Date()
|
||||
};
|
||||
|
||||
await userCollection.insertOne(newUser);
|
||||
const result = await userCollection.insertOne(newUser);
|
||||
// console.log("userCollection.insertOne() result", result);
|
||||
const userId = result.insertedId.toString();
|
||||
|
||||
const folderTitle = 'Dossier par Défaut';
|
||||
const userId = newUser._id.toString();
|
||||
await Folders.create(folderTitle, userId);
|
||||
await this.folders.create(folderTitle, userId);
|
||||
|
||||
// TODO: verif if inserted properly...
|
||||
return result;
|
||||
}
|
||||
|
||||
async login(email, password) {
|
||||
await db.connect()
|
||||
const conn = db.getConnection();
|
||||
await this.db.connect()
|
||||
const conn = this.db.getConnection();
|
||||
|
||||
const userCollection = conn.collection('users');
|
||||
|
||||
|
|
@ -74,8 +78,8 @@ class Users {
|
|||
}
|
||||
|
||||
async changePassword(email, newPassword) {
|
||||
await db.connect()
|
||||
const conn = db.getConnection();
|
||||
await this.db.connect()
|
||||
const conn = this.db.getConnection();
|
||||
|
||||
const userCollection = conn.collection('users');
|
||||
|
||||
|
|
@ -89,8 +93,8 @@ class Users {
|
|||
}
|
||||
|
||||
async delete(email) {
|
||||
await db.connect()
|
||||
const conn = db.getConnection();
|
||||
await this.db.connect()
|
||||
const conn = this.db.getConnection();
|
||||
|
||||
const userCollection = conn.collection('users');
|
||||
|
||||
|
|
@ -102,8 +106,8 @@ class Users {
|
|||
}
|
||||
|
||||
async getId(email) {
|
||||
await db.connect()
|
||||
const conn = db.getConnection();
|
||||
await this.db.connect()
|
||||
const conn = this.db.getConnection();
|
||||
|
||||
const userCollection = conn.collection('users');
|
||||
|
||||
|
|
@ -118,4 +122,4 @@ class Users {
|
|||
|
||||
}
|
||||
|
||||
module.exports = new Users;
|
||||
module.exports = Users;
|
||||
|
|
|
|||
35
server/models/utils.js
Normal file
35
server/models/utils.js
Normal file
|
|
@ -0,0 +1,35 @@
|
|||
// utils.js
|
||||
async function generateUniqueTitle(baseTitle, existsCallback) {
|
||||
console.log(`generateUniqueTitle(${baseTitle})`);
|
||||
let newTitle = baseTitle;
|
||||
let counter = 1;
|
||||
|
||||
const titleRegex = /(.*?)(\((\d+)\))?$/;
|
||||
const match = baseTitle.match(titleRegex);
|
||||
if (match) {
|
||||
baseTitle = match[1].trim();
|
||||
counter = match[3] ? parseInt(match[3], 10) + 1 : 1;
|
||||
}
|
||||
|
||||
// If the base title does not end with a parentheses expression, start with "(1)"
|
||||
if (!match[2]) {
|
||||
newTitle = `${baseTitle} (${counter})`;
|
||||
} else {
|
||||
// else increment the counter in the parentheses expression as a first try
|
||||
newTitle = `${baseTitle} (${counter})`;
|
||||
}
|
||||
|
||||
console.log(`first check of newTitle: ${newTitle}`);
|
||||
|
||||
while (await existsCallback(newTitle)) {
|
||||
counter++;
|
||||
newTitle = `${baseTitle} (${counter})`;
|
||||
console.log(`trying newTitle: ${newTitle}`);
|
||||
}
|
||||
|
||||
return newTitle;
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
generateUniqueTitle
|
||||
};
|
||||
1
server/package-lock.json
generated
1
server/package-lock.json
generated
|
|
@ -22,6 +22,7 @@
|
|||
},
|
||||
"devDependencies": {
|
||||
"jest": "^29.7.0",
|
||||
"jest-mock": "^29.7.0",
|
||||
"nodemon": "^3.0.1",
|
||||
"supertest": "^6.3.4"
|
||||
},
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@
|
|||
"build": "webpack --config webpack.config.js",
|
||||
"start": "node app.js",
|
||||
"dev": "nodemon app.js",
|
||||
"test": "jest"
|
||||
"test": "jest --colors"
|
||||
},
|
||||
"keywords": [],
|
||||
"author": "",
|
||||
|
|
@ -26,6 +26,7 @@
|
|||
},
|
||||
"devDependencies": {
|
||||
"jest": "^29.7.0",
|
||||
"jest-mock": "^29.7.0",
|
||||
"nodemon": "^3.0.1",
|
||||
"supertest": "^6.3.4"
|
||||
},
|
||||
|
|
|
|||
|
|
@ -1,18 +1,22 @@
|
|||
const express = require('express');
|
||||
const router = express.Router();
|
||||
const jwt = require('../middleware/jwtToken.js');
|
||||
const folders = require('../app.js').folders;
|
||||
|
||||
const foldersController = require('../controllers/folders.js')
|
||||
|
||||
router.post("/create", jwt.authenticate, foldersController.create);
|
||||
router.get("/getUserFolders", jwt.authenticate, foldersController.getUserFolders);
|
||||
router.get("/getFolderContent/:folderId", jwt.authenticate, foldersController.getFolderContent);
|
||||
router.delete("/delete/:folderId", jwt.authenticate, foldersController.delete);
|
||||
router.put("/rename", jwt.authenticate, foldersController.rename);
|
||||
router.post("/create", jwt.authenticate, folders.create);
|
||||
router.get("/getUserFolders", jwt.authenticate, folders.getUserFolders);
|
||||
router.get("/getFolderContent/:folderId", jwt.authenticate, folders.getFolderContent);
|
||||
router.delete("/delete/:folderId", jwt.authenticate, folders.delete);
|
||||
router.put("/rename", jwt.authenticate, folders.rename);
|
||||
|
||||
//router.post("/duplicate", jwt.authenticate, foldersController.duplicate);
|
||||
router.post("/duplicate", jwt.authenticate, foldersController.duplicate);
|
||||
router.post("/duplicate", jwt.authenticate, folders.duplicate);
|
||||
|
||||
router.post("/copy/:folderId", jwt.authenticate, foldersController.copy);
|
||||
router.post("/copy/:folderId", jwt.authenticate, folders.copy);
|
||||
|
||||
module.exports = router;
|
||||
module.exports = router;
|
||||
|
||||
// export also folders (the controller)
|
||||
module.exports.folders = folders;
|
||||
|
||||
// Refer to folders using: const folders = require('../controllers/folders.js').folders;
|
||||
|
|
|
|||
|
|
@ -1,15 +1,15 @@
|
|||
const express = require('express');
|
||||
const router = express.Router();
|
||||
const images = require('../app.js').images;
|
||||
|
||||
const jwt = require('../middleware/jwtToken.js');
|
||||
const imagesController = require('../controllers/images.js')
|
||||
|
||||
// For getting the image out of the form data
|
||||
const multer = require('multer');
|
||||
const storage = multer.memoryStorage();
|
||||
const upload = multer({ storage: storage });
|
||||
|
||||
router.post("/upload", jwt.authenticate, upload.single('image'), imagesController.upload);
|
||||
router.get("/get/:id", imagesController.get);
|
||||
router.post("/upload", jwt.authenticate, upload.single('image'), images.upload);
|
||||
router.get("/get/:id", images.get);
|
||||
|
||||
module.exports = router;
|
||||
module.exports = router;
|
||||
|
|
|
|||
|
|
@ -1,19 +1,22 @@
|
|||
const express = require('express');
|
||||
const router = express.Router();
|
||||
|
||||
const quizzes = require('../app.js').quizzes;
|
||||
const jwt = require('../middleware/jwtToken.js');
|
||||
const quizController = require('../controllers/quiz.js')
|
||||
|
||||
router.post("/create", jwt.authenticate, quizController.create);
|
||||
router.get("/get/:quizId", jwt.authenticate, quizController.get);
|
||||
router.delete("/delete/:quizId", jwt.authenticate, quizController.delete);
|
||||
router.put("/update", jwt.authenticate, quizController.update);
|
||||
router.put("/move", jwt.authenticate, quizController.move);
|
||||
if (!quizzes) {
|
||||
console.error("quizzes is not defined");
|
||||
}
|
||||
|
||||
router.post("/duplicate", jwt.authenticate, quizController.duplicate);
|
||||
router.post("/copy/:quizId", jwt.authenticate, quizController.copy);
|
||||
router.put("/Share", jwt.authenticate, quizController.Share);
|
||||
router.get("/getShare/:quizId", jwt.authenticate, quizController.getShare);
|
||||
router.post("/receiveShare", jwt.authenticate, quizController.receiveShare);
|
||||
router.post("/create", jwt.authenticate, quizzes.create);
|
||||
router.get("/get/:quizId", jwt.authenticate, quizzes.get);
|
||||
router.delete("/delete/:quizId", jwt.authenticate, quizzes.delete);
|
||||
router.put("/update", jwt.authenticate, quizzes.update);
|
||||
router.put("/move", jwt.authenticate, quizzes.move);
|
||||
|
||||
module.exports = router;
|
||||
router.post("/duplicate", jwt.authenticate, quizzes.duplicate);
|
||||
router.post("/copy/:quizId", jwt.authenticate, quizzes.copy);
|
||||
router.put("/Share", jwt.authenticate, quizzes.share);
|
||||
router.get("/getShare/:quizId", jwt.authenticate, quizzes.getShare);
|
||||
router.post("/receiveShare", jwt.authenticate, quizzes.receiveShare);
|
||||
|
||||
module.exports = router;
|
||||
|
|
|
|||
|
|
@ -1,13 +1,12 @@
|
|||
const express = require('express');
|
||||
const router = express.Router();
|
||||
|
||||
const users = require('../app.js').users;
|
||||
const jwt = require('../middleware/jwtToken.js');
|
||||
const usersController = require('../controllers/users.js')
|
||||
|
||||
router.post("/register", usersController.register);
|
||||
router.post("/login", usersController.login);
|
||||
router.post("/reset-password", usersController.resetPassword);
|
||||
router.post("/change-password", jwt.authenticate, usersController.changePassword);
|
||||
router.post("/delete-user", jwt.authenticate, usersController.delete);
|
||||
router.post("/register", users.register);
|
||||
router.post("/login", users.login);
|
||||
router.post("/reset-password", users.resetPassword);
|
||||
router.post("/change-password", jwt.authenticate, users.changePassword);
|
||||
router.post("/delete-user", jwt.authenticate, users.delete);
|
||||
|
||||
module.exports = router;
|
||||
module.exports = router;
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@ const setupWebsocket = (io) => {
|
|||
console.log("Connection limit reached. Disconnecting client.");
|
||||
socket.emit(
|
||||
"join-failure",
|
||||
"Le nombre maximum de connexion a été atteint"
|
||||
"Le nombre maximum de connexions a été atteint"
|
||||
);
|
||||
socket.disconnect(true);
|
||||
return;
|
||||
|
|
|
|||
Loading…
Reference in a new issue