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;