Quiz model class, using a Repository pattern with mocked repository and tests

This commit is contained in:
C. Fuhrman 2024-09-28 17:29:21 -04:00
parent 83a23e4f8c
commit 7abe517c8f
8 changed files with 476 additions and 159 deletions

View file

@ -0,0 +1,125 @@
import { MongoClient, Db, ObjectId } from 'mongodb';
import { Quiz } from '../models/quiz';
import QuizRepository from '../repositories/quizRepository';
jest.mock('../repositories/quizRepository');
describe('Quiz Class', () => {
let connection: MongoClient;
let db: Db;
let quizRepository: QuizRepository;
beforeAll(async () => {
connection = {
db: jest.fn().mockReturnThis(),
collection: jest.fn().mockReturnThis(),
findOne: jest.fn(),
insertOne: jest.fn(),
deleteOne: jest.fn(),
deleteMany: jest.fn(),
updateOne: jest.fn(),
} as unknown as MongoClient;
db = connection.db();
quizRepository = new QuizRepository();
(quizRepository as any).db = { getConnection: () => db };
});
beforeEach(() => {
jest.clearAllMocks();
});
it('should create a new quiz', async () => {
const quiz = new Quiz('folderId', 'userId', 'title', 'content', quizRepository);
const insertedId = new ObjectId();
jest.spyOn(quizRepository, 'createQuiz').mockResolvedValue(insertedId);
const result = await quiz.create();
expect(result).toEqual(insertedId);
expect(quizRepository.createQuiz).toHaveBeenCalledWith(quiz);
});
it('should get the owner of a quiz', async () => {
const quizId = new ObjectId().toHexString();
const userId = 'userId';
jest.spyOn(quizRepository, 'getOwner').mockResolvedValue(userId);
const quiz = new Quiz('folderId', 'userId', 'title', 'content', quizRepository);
const result = await quiz.getOwner(quizId);
expect(result).toEqual(userId);
expect(quizRepository.getOwner).toHaveBeenCalledWith(quizId);
});
it('should get the content of a quiz', async () => {
const quizId = new ObjectId().toHexString();
const content = 'content';
jest.spyOn(quizRepository, 'getContent').mockResolvedValue(content);
const quiz = new Quiz('folderId', 'userId', 'title', 'content', quizRepository);
const result = await quiz.getContent(quizId);
expect(result).toEqual(content);
expect(quizRepository.getContent).toHaveBeenCalledWith(quizId);
});
it('should delete a quiz', async () => {
const quizId = new ObjectId().toHexString();
jest.spyOn(quizRepository, 'delete').mockResolvedValue(true);
const quiz = new Quiz('folderId', 'userId', 'title', 'content', quizRepository);
const result = await quiz.delete(quizId);
expect(result).toBe(true);
expect(quizRepository.delete).toHaveBeenCalledWith(quizId);
});
it('should update a quiz', async () => {
const quizId = new ObjectId().toHexString();
const updateData = { title: 'new title' };
jest.spyOn(quizRepository, 'update').mockResolvedValue(true);
const quiz = new Quiz('folderId', 'userId', 'title', 'content', quizRepository);
const result = await quiz.update(quizId, updateData);
expect(result).toBe(true);
expect(quizRepository.update).toHaveBeenCalledWith(quizId, updateData);
});
it('should move a quiz to a new folder', async () => {
const quizId = new ObjectId().toHexString();
const newFolderId = 'newFolderId';
jest.spyOn(quizRepository, 'move').mockResolvedValue(true);
const quiz = new Quiz('folderId', 'userId', 'title', 'content', quizRepository);
const result = await quiz.move(quizId, newFolderId);
expect(result).toBe(true);
expect(quizRepository.move).toHaveBeenCalledWith(quizId, newFolderId);
});
it('should duplicate a quiz', async () => {
const quizId = new ObjectId().toHexString();
const newQuizId = new ObjectId();
jest.spyOn(quizRepository, 'duplicate').mockResolvedValue(newQuizId);
const quiz = new Quiz('folderId', 'userId', 'title', 'content', quizRepository);
const result = await quiz.duplicate(quizId);
expect(result).toEqual(newQuizId);
expect(quizRepository.duplicate).toHaveBeenCalledWith(quizId);
});
it('should check if a quiz exists', async () => {
const title = 'title';
const userId = 'userId';
jest.spyOn(quizRepository, 'quizExists').mockResolvedValue(true);
const quiz = new Quiz('folderId', 'userId', 'title', 'content', quizRepository);
const result = await quiz.quizExists(title, userId);
expect(result).toBe(true);
expect(quizRepository.quizExists).toHaveBeenCalledWith(title, userId);
});
});

View file

@ -1,22 +1,25 @@
const { MongoClient } = require('mongodb');
const dotenv = require('dotenv')
import { MongoClient, Db } from 'mongodb';
import dotenv from 'dotenv';
dotenv.config();
class DBConnection {
private mongoURI: string;
private databaseName: string;
private connection: MongoClient | null;
constructor() {
this.mongoURI = process.env.MONGO_URI;
this.databaseName = process.env.MONGO_DATABASE;
this.mongoURI = process.env.MONGO_URI || '';
this.databaseName = process.env.MONGO_DATABASE || '';
this.connection = null;
}
async connect() {
async connect(): Promise<void> {
const client = new MongoClient(this.mongoURI);
this.connection = await client.connect();
}
getConnection() {
getConnection(): Db {
if (!this.connection) {
throw new Error('Connexion MongoDB non établie');
}
@ -24,5 +27,4 @@ class DBConnection {
}
}
const instance = new DBConnection();
module.exports = instance;
export default DBConnection;

View file

@ -1,15 +1,18 @@
const nodemailer = require('nodemailer');
const dotenv = require('dotenv');
import nodemailer, { Transporter, SendMailOptions, SentMessageInfo } from 'nodemailer';
import dotenv from 'dotenv';
dotenv.config();
class Emailer {
private senderEmail: string;
private psw: string;
private transporter: Transporter;
constructor() {
this.senderEmail = process.env.SENDER_EMAIL;
this.psw = process.env.EMAIL_PSW;
this.senderEmail = process.env.SENDER_EMAIL || '';
this.psw = process.env.EMAIL_PSW || '';
this.transporter = nodemailer.createTransport({
service: process.env.EMAIL_SERVICE,
service: process.env.EMAIL_SERVICE || '',
auth: {
user: this.senderEmail,
pass: this.psw
@ -17,33 +20,44 @@ class Emailer {
});
}
registerConfirmation(email) {
this.transporter.sendMail({
private handleEmailResult(error: Error | null, info: SentMessageInfo): void {
if (error) {
console.error('Error sending email:', error);
} else {
console.log('Email sent:', info.response);
}
}
registerConfirmation(email: string): void {
const mailOptions: SendMailOptions = {
from: this.senderEmail,
to: email,
subject: 'Confirmation de compte',
text: 'Votre compte a été créé avec succès.'
});
// Add other email options here if needed
};
this.transporter.sendMail(mailOptions, this.handleEmailResult);
}
newPasswordConfirmation(email,newPassword) {
this.transporter.sendMail({
newPasswordConfirmation(email: string, newPassword: string): void {
const mailOptions: SendMailOptions = {
from: this.senderEmail,
to: email,
subject: 'Mot de passe temporaire',
text: 'Votre nouveau mot de passe temporaire est : ' + newPassword
});
};
this.transporter.sendMail(mailOptions, this.handleEmailResult);
}
quizShare(email, link) {
this.transporter.sendMail({
quizShare(email: string, link: string): void {
const mailOptions: SendMailOptions = {
from: this.senderEmail,
to: email,
subject: 'Un quiz vous a été transféré !',
text: 'Veuillez suivre ce lien pour ajouter ce quiz à votre compte. '+ link
});
};
this.transporter.sendMail(mailOptions, this.handleEmailResult);
}
}
module.exports = new Emailer();
export default Emailer;

View file

@ -1,133 +1,58 @@
const db = require('../config/db.js')
const { ObjectId } = require('mongodb');
import { ObjectId } from 'mongodb';
import QuizRepository from '../repositories/quizRepository';
class Quiz {
export class Quiz {
private repository: QuizRepository;
folderId: string;
userId: string;
title: string;
content: string;
created_at: Date;
updated_at: Date;
async create(title, content, folderId, userId) {
await db.connect()
const conn = db.getConnection();
const quizCollection = conn.collection('files');
const existingQuiz = await quizCollection.findOne({ title: title, folderId: folderId, userId: userId })
if (existingQuiz) return null;
const newQuiz = {
folderId: folderId,
userId: userId,
title: title,
content: content,
created_at: new Date(),
updated_at: new Date()
constructor(folderId: string, userId: string, title: string, content: string, repository?: QuizRepository) {
this.repository = repository || new QuizRepository();
this.folderId = folderId;
this.userId = userId;
this.title = title;
this.content = content;
this.created_at = new Date();
this.updated_at = new Date();
}
const result = await quizCollection.insertOne(newQuiz);
return result.insertedId;
async create(): Promise<ObjectId | null> {
return await this.repository.createQuiz(this);
}
async getOwner(quizId) {
await db.connect()
const conn = db.getConnection();
const quizCollection = conn.collection('files');
const quiz = await quizCollection.findOne({ _id: new ObjectId(quizId) });
return quiz.userId;
async getOwner(quizId: string): Promise<string | null> {
return await this.repository.getOwner(quizId);
}
async getContent(quizId) {
await db.connect()
const conn = db.getConnection();
const quizCollection = conn.collection('files');
const quiz = await quizCollection.findOne({ _id: new ObjectId(quizId) });
return quiz;
async getContent(quizId: string): Promise<string | null> {
return await this.repository.getContent(quizId);
}
async delete(quizId) {
await db.connect()
const conn = db.getConnection();
const quizCollection = conn.collection('files');
const result = await quizCollection.deleteOne({ _id: new ObjectId(quizId) });
if (result.deletedCount != 1) return false;
return true;
}
async deleteQuizzesByFolderId(folderId) {
await db.connect();
const conn = db.getConnection();
const quizzesCollection = conn.collection('files');
// Delete all quizzes with the specified folderId
await quizzesCollection.deleteMany({ folderId: folderId });
async delete(quizId: string): Promise<boolean> {
return await this.repository.delete(quizId);
}
async update(quizId, newTitle, newContent) {
await db.connect()
const conn = 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;
return true
async deleteQuizzes(folderId: string): Promise<number> {
return await this.repository.deleteQuizzes(folderId);
}
async move(quizId, newFolderId) {
await db.connect()
const conn = db.getConnection();
const quizCollection = conn.collection('files');
const result = await quizCollection.updateOne({ _id: new ObjectId(quizId) }, { $set: { folderId: newFolderId } });
if (result.modifiedCount != 1) return false;
return true
async update(quizId: string, updateData: Partial<Quiz>): Promise<boolean> {
return await this.repository.update(quizId, updateData);
}
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++;
}
//console.log(newQuizTitle);
const newQuizId = await this.create(newQuizTitle, sourceQuiz.content,sourceQuiz.folderId, userId);
if (!newQuizId) {
throw new Error('Failed to create a duplicate quiz.');
async move(quizId: string, newFolderId: string): Promise<boolean> {
return await this.repository.move(quizId, newFolderId);
}
return newQuizId;
async duplicate(quizId: string): Promise<ObjectId | null> {
return await this.repository.duplicate(quizId);
}
async quizExists(title, userId) {
await db.connect();
const conn = db.getConnection();
const filesCollection = conn.collection('files');
const existingFolder = await filesCollection.findOne({ title: title, userId: userId });
return existingFolder !== null;
async quizExists(title: string, userId: string): Promise<boolean> {
return await this.repository.quizExists(title, userId);
}
}
module.exports = new Quiz;

162
server/package-lock.json generated
View file

@ -36,6 +36,7 @@
"jest": "^29.7.0",
"nodemon": "^3.0.1",
"supertest": "^6.3.4",
"ts-jest": "^29.2.5",
"typescript": "^5.6.2"
},
"engines": {
@ -1665,6 +1666,12 @@
"integrity": "sha512-BSHWgDSAiKs50o2Re8ppvp3seVHXSRM44cdSsT9FfNEUUZLOGWVCsiWaRPWM1Znn+mqZ1OfVZ3z3DWEzSp7hRA==",
"dev": true
},
"node_modules/async": {
"version": "3.2.6",
"resolved": "https://registry.npmjs.org/async/-/async-3.2.6.tgz",
"integrity": "sha512-htCUDlxyyCLMgaM3xXg0C0LW2xqfuQ6p05pCEIsXuyQ+a1koYKTuBMzRNwmybfLgvJDMd0r1LTn4+E0Ti6C2AA==",
"dev": true
},
"node_modules/asynckit": {
"version": "0.4.0",
"resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz",
@ -1898,6 +1905,18 @@
"node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7"
}
},
"node_modules/bs-logger": {
"version": "0.2.6",
"resolved": "https://registry.npmjs.org/bs-logger/-/bs-logger-0.2.6.tgz",
"integrity": "sha512-pd8DCoxmbgc7hyPKOvxtqNcjYoOsABPQdcCUjGp3d42VR2CX1ORhk2A87oqqu5R1kk+76nsxZupkmyd+MVtCog==",
"dev": true,
"dependencies": {
"fast-json-stable-stringify": "2.x"
},
"engines": {
"node": ">= 6"
}
},
"node_modules/bser": {
"version": "2.1.1",
"resolved": "https://registry.npmjs.org/bser/-/bser-2.1.1.tgz",
@ -2464,6 +2483,21 @@
"resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz",
"integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow=="
},
"node_modules/ejs": {
"version": "3.1.10",
"resolved": "https://registry.npmjs.org/ejs/-/ejs-3.1.10.tgz",
"integrity": "sha512-UeJmFfOrAQS8OJWPZ4qtgHyWExa088/MtK5UEyoJGFH67cDEXkZSviOiKRCZ4Xij0zxI3JECgYs3oKx+AizQBA==",
"dev": true,
"dependencies": {
"jake": "^10.8.5"
},
"bin": {
"ejs": "bin/cli.js"
},
"engines": {
"node": ">=0.10.0"
}
},
"node_modules/electron-to-chromium": {
"version": "1.4.574",
"resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.574.tgz",
@ -2767,6 +2801,36 @@
"bser": "2.1.1"
}
},
"node_modules/filelist": {
"version": "1.0.4",
"resolved": "https://registry.npmjs.org/filelist/-/filelist-1.0.4.tgz",
"integrity": "sha512-w1cEuf3S+DrLCQL7ET6kz+gmlJdbq9J7yXCSjK/OZCPA+qEN1WyF4ZAf0YYJa4/shHJra2t/d/r8SV4Ji+x+8Q==",
"dev": true,
"dependencies": {
"minimatch": "^5.0.1"
}
},
"node_modules/filelist/node_modules/brace-expansion": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz",
"integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==",
"dev": true,
"dependencies": {
"balanced-match": "^1.0.0"
}
},
"node_modules/filelist/node_modules/minimatch": {
"version": "5.1.6",
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.6.tgz",
"integrity": "sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==",
"dev": true,
"dependencies": {
"brace-expansion": "^2.0.1"
},
"engines": {
"node": ">=10"
}
},
"node_modules/fill-range": {
"version": "7.1.1",
"resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz",
@ -3443,6 +3507,24 @@
"node": ">=8"
}
},
"node_modules/jake": {
"version": "10.9.2",
"resolved": "https://registry.npmjs.org/jake/-/jake-10.9.2.tgz",
"integrity": "sha512-2P4SQ0HrLQ+fw6llpLnOaGAvN2Zu6778SJMrCUwns4fOoG9ayrTiZk3VV8sCPkVZF8ab0zksVpS8FDY5pRCNBA==",
"dev": true,
"dependencies": {
"async": "^3.2.3",
"chalk": "^4.0.2",
"filelist": "^1.0.4",
"minimatch": "^3.1.2"
},
"bin": {
"jake": "bin/cli.js"
},
"engines": {
"node": ">=10"
}
},
"node_modules/jest": {
"version": "29.7.0",
"resolved": "https://registry.npmjs.org/jest/-/jest-29.7.0.tgz",
@ -4261,22 +4343,17 @@
"resolved": "https://registry.npmjs.org/lodash.isstring/-/lodash.isstring-4.0.1.tgz",
"integrity": "sha512-0wJxfxH1wgO3GrbuP+dTTk7op+6L41QCXbGINEmD+ny/G/eCqGzxyCsh7159S+mgDDcoarnBw6PC1PS5+wUGgw=="
},
"node_modules/lodash.memoize": {
"version": "4.1.2",
"resolved": "https://registry.npmjs.org/lodash.memoize/-/lodash.memoize-4.1.2.tgz",
"integrity": "sha512-t7j+NzmgnQzTAYXcsHYLgimltOV1MXHtlOWf6GjL9Kj8GK5FInw5JotxvbOs+IvV1/Dzo04/fCGfLVs7aXb4Ag==",
"dev": true
},
"node_modules/lodash.once": {
"version": "4.1.1",
"resolved": "https://registry.npmjs.org/lodash.once/-/lodash.once-4.1.1.tgz",
"integrity": "sha512-Sb487aTOCr9drQVL8pIxOzVhafOjZN9UU54hiN8PU3uAiSV7lx1yYNpbNmex2PK6dSJoNTSJUUswT651yww3Mg=="
},
"node_modules/lru-cache": {
"version": "6.0.0",
"resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz",
"integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==",
"dependencies": {
"yallist": "^4.0.0"
},
"engines": {
"node": ">=10"
}
},
"node_modules/make-dir": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/make-dir/-/make-dir-4.0.0.tgz",
@ -4292,6 +4369,12 @@
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/make-error": {
"version": "1.3.6",
"resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz",
"integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==",
"dev": true
},
"node_modules/makeerror": {
"version": "1.0.12",
"resolved": "https://registry.npmjs.org/makeerror/-/makeerror-1.0.12.tgz",
@ -5142,12 +5225,9 @@
"integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg=="
},
"node_modules/semver": {
"version": "7.5.4",
"resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz",
"integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==",
"dependencies": {
"lru-cache": "^6.0.0"
},
"version": "7.6.3",
"resolved": "https://registry.npmjs.org/semver/-/semver-7.6.3.tgz",
"integrity": "sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A==",
"bin": {
"semver": "bin/semver.js"
},
@ -5744,6 +5824,54 @@
"node": ">=14"
}
},
"node_modules/ts-jest": {
"version": "29.2.5",
"resolved": "https://registry.npmjs.org/ts-jest/-/ts-jest-29.2.5.tgz",
"integrity": "sha512-KD8zB2aAZrcKIdGk4OwpJggeLcH1FgrICqDSROWqlnJXGCXK4Mn6FcdK2B6670Xr73lHMG1kHw8R87A0ecZ+vA==",
"dev": true,
"dependencies": {
"bs-logger": "^0.2.6",
"ejs": "^3.1.10",
"fast-json-stable-stringify": "^2.1.0",
"jest-util": "^29.0.0",
"json5": "^2.2.3",
"lodash.memoize": "^4.1.2",
"make-error": "^1.3.6",
"semver": "^7.6.3",
"yargs-parser": "^21.1.1"
},
"bin": {
"ts-jest": "cli.js"
},
"engines": {
"node": "^14.15.0 || ^16.10.0 || ^18.0.0 || >=20.0.0"
},
"peerDependencies": {
"@babel/core": ">=7.0.0-beta.0 <8",
"@jest/transform": "^29.0.0",
"@jest/types": "^29.0.0",
"babel-jest": "^29.0.0",
"jest": "^29.0.0",
"typescript": ">=4.3 <6"
},
"peerDependenciesMeta": {
"@babel/core": {
"optional": true
},
"@jest/transform": {
"optional": true
},
"@jest/types": {
"optional": true
},
"babel-jest": {
"optional": true
},
"esbuild": {
"optional": true
}
}
},
"node_modules/type-detect": {
"version": "4.0.8",
"resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.0.8.tgz",

View file

@ -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": "",
@ -40,16 +40,18 @@
"jest": "^29.7.0",
"nodemon": "^3.0.1",
"supertest": "^6.3.4",
"ts-jest": "^29.2.5",
"typescript": "^5.6.2"
},
"engines": {
"node": "18.x"
},
"jest": {
"preset": "ts-jest",
"testEnvironment": "node",
"testMatch": [
"**/__tests__/**/*.js?(x)",
"**/?(*.)+(spec|test).js?(x)"
"**/__tests__/**/*.ts?(x)",
"**/?(*.)+(spec|test).ts?(x)"
]
}
}

View file

@ -0,0 +1,121 @@
import { Db, ObjectId } from 'mongodb';
import DBConnection from '../config/db';
import { Quiz } from '../models/quiz';
class QuizRepository {
private db: DBConnection;
constructor() {
this.db = new DBConnection();
}
async createQuiz(quiz: Quiz): Promise<ObjectId | null> {
await this.db.connect();
const conn: Db = this.db.getConnection();
const quizCollection = conn.collection('files');
const existingQuiz = await quizCollection.findOne({ title: quiz.title, folderId: quiz.folderId, userId: quiz.userId });
if (existingQuiz) return null;
const result = await quizCollection.insertOne(quiz);
return result.insertedId;
}
async getOwner(quizId: string): Promise<string | null> {
await this.db.connect();
const conn: Db = this.db.getConnection();
const quizCollection = conn.collection('files');
const quiz = await quizCollection.findOne({ _id: new ObjectId(quizId) });
return quiz ? quiz.userId : null;
}
async getContent(quizId: string): Promise<string | null> {
await this.db.connect();
const conn: Db = this.db.getConnection();
const quizCollection = conn.collection('files');
const quiz = await quizCollection.findOne({ _id: new ObjectId(quizId) });
return quiz ? quiz.content : null;
}
async delete(quizId: string): Promise<boolean> {
await this.db.connect();
const conn: Db = this.db.getConnection();
const quizCollection = conn.collection('files');
const result = await quizCollection.deleteOne({ _id: new ObjectId(quizId) });
return result.deletedCount === 1;
}
async deleteQuizzes(folderId: string): Promise<number> {
await this.db.connect();
const conn: Db = this.db.getConnection();
const quizCollection = conn.collection('files');
const result = await quizCollection.deleteMany({ folderId: folderId });
return result.deletedCount || 0;
}
async update(quizId: string, updateData: Partial<Quiz>): Promise<boolean> {
await this.db.connect();
const conn: Db = this.db.getConnection();
const quizCollection = conn.collection('files');
const result = await quizCollection.updateOne(
{ _id: new ObjectId(quizId) },
{ $set: { ...updateData, updated_at: new Date() } }
);
return result.modifiedCount === 1;
}
async move(quizId: string, newFolderId: string): Promise<boolean> {
return await this.update(quizId, { folderId: newFolderId });
}
async duplicate(quizId: string): Promise<ObjectId | null> {
await this.db.connect();
const conn: Db = this.db.getConnection();
const quizCollection = conn.collection('files');
const quiz = await quizCollection.findOne({ _id: new ObjectId(quizId) });
if (!quiz) return null;
const newQuiz = {
...quiz,
_id: undefined,
created_at: new Date(),
updated_at: new Date()
};
const result = await quizCollection.insertOne(newQuiz);
return result.insertedId;
}
async quizExists(title: string, userId: string): Promise<boolean> {
await this.db.connect();
const conn: Db = this.db.getConnection();
const quizCollection = conn.collection('files');
const existingQuiz = await quizCollection.findOne({ title: title, userId: userId });
return existingQuiz !== null;
}
}
export default QuizRepository;

View file

@ -107,6 +107,6 @@
// "skipDefaultLibCheck": true, /* Skip type checking .d.ts files that are included with TypeScript. */
"skipLibCheck": true /* Skip type checking all .d.ts files. */
},
"include": ["**/*.ts"],
"include": ["./**/*.ts"],
"exclude": ["node_modules", "dist"]
}