const { MongoClient, ObjectId } = require('mongodb'); // require the mongodb driver /** * Uses mongodb v4.2+ - [API Documentation](http://mongodb.github.io/node-mongodb-native/4.2/) * Database wraps a mongoDB connection to provide a higher-level abstraction layer * for manipulating the objects in our cpen322 app. */ function Database(mongoUrl, dbName){ if (!(this instanceof Database)) return new Database(mongoUrl, dbName); this.connected = new Promise((resolve, reject) => { const client = new MongoClient(mongoUrl); client.connect() .then(() => { // Ping the dbName to ensure it exists return client.db(dbName).command({ ping: 1 }); }) .then(() => { console.log('[MongoClient] Connected to ' + mongoUrl + '/' + dbName); resolve(client.db(dbName)); }) .catch((err) => { reject(err); }); }); this.status = () => this.connected.then( db => ({ error: null, url: mongoUrl, db: dbName }), err => ({ error: err }) ); } Database.prototype.getRooms = function(){ return this.connected.then(db => new Promise((resolve, reject) => { db.collection('chatrooms').find({}).toArray() .then(resolve, reject); }) ) } Database.prototype.getRoom = function(room_id){ return this.connected.then(db => new Promise((resolve, reject) => { let objectId; try { objectId = new ObjectId(room_id); } catch (error) { objectId = null; } const findByObjectId = objectId => { return db.collection('chatrooms').findOne({ _id: objectId }); }; const findByStringId = () => { return db.collection('chatrooms').findOne({ _id: room_id }); }; // If we can create an ObjectId from room_id if (objectId) { findByObjectId(objectId).then(document => { if (document) { // If a document is found with ObjectID, resolve it return resolve(document); } else { // If no document is found, fallback to string ID findByStringId().then(resolve, reject); } }, reject); } else { // If room_id cannot be converted to ObjectID, search by string ID findByStringId().then(resolve, reject); } }) ) } Database.prototype.addRoom = function(room){ return this.connected.then(db => new Promise(async (resolve, reject) => { if(!room.name){ return reject(new Error("Room name is required.")); } try{ const result = await db.collection('chatrooms').insertOne(room); const roomObj = await this.getRoom(result.insertedId); return resolve(roomObj); } catch(error) { return reject(error) } }) ) } Database.prototype.getLastConversation = function(room_id, before = Date.now()){ return this.connected.then(db => new Promise((resolve, reject) => { db.collection('conversations').find({ room_id: room_id, timestamp: { $lt: before } }) .sort({ timestamp: -1 }) // Sort timestamp in descending order .limit(1) // Just grab one conversation .toArray().then(conversations => { if(conversations.length > 0){ return resolve(conversations[0]); } else { return resolve(null); } }).catch(reject); }) ) } Database.prototype.addConversation = function(conversation){ return this.connected.then(db => new Promise(async (resolve, reject) => { if(!conversation.room_id || !conversation.timestamp || !conversation.messages){ return reject(new Error("Missing property in conversation.")); } try{ const result = await db.collection('conversations').insertOne(conversation); const conversationObj = await db.collection('conversations').findOne({ _id: result.insertedId }); return resolve(conversationObj); } catch(error) { return reject(error) } }) ) } Database.prototype.getUser = function(username){ return this.connected.then(db => new Promise(async (resolve, reject) => { try{ const user = await db.collection('users').findOne({ username }); return resolve(user); } catch(error) { return reject(error) } }) ) } Database.prototype.incrementInappropriateAttempts = function(username){ return this.connected.then(db => db.collection('users').updateOne( { username: username }, { $inc: { inapContentAttempts: 1 } } ).catch(error => { throw error; }) ); } Database.prototype.getInappropriateAttempts = function(username){ return this.connected.then(db => db.collection('users').findOne({ username: username }) .then(user => { const attempts = user ? (user.inapContentAttempts || 0) : 0; return attempts; }).catch(error => { throw error }) ); } module.exports = Database;