chat-application-322 / Database.js
Database.js
Raw
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;