import mongodb from 'mongodb';
const { MongoClient: MongoClient } = mongodb;
export default class DatabaseClient {
/**
* Initializes a new instance of a DatabaseClient
*/
constructor(label, options = {
useNewUrlParser: true,
useUnifiedTopology: true
}, uri) {
// A name for the client
this.label = label
// The connection URI
this.URI = process.env.MONGO_DB_URI || uri
// The MongoClient this belongs to
this.client = new MongoClient(this.URI, options)
// Development mode converts this class to a mock shell to avoid throwing errors without a URI string
this.developmentMode = this.URI == undefined
}
/**
* Sets up a DatabaseClient for use by connecting to the database instance
*/
async initialize() {
if(this.developmentMode) {
this.mock()
return
}
return await this.connect().catch(err => {
console.error(err)
this.initialize()
})
}
/**
* Mocks a MongoDB database
*/
mock() {
this.client = {
connect: () => {},
database: {
collection: () => {
return {
findOne: new Promise().resolve({}),
find: new Promise().resolve({}),
insertOne: new Promise().resolve({})
}
}
},
db: () => {}
}
}
/**
* Connects to the item database
*/
connect() {
return new Promise((resolve, reject) => {
this.client.connect(err => {
if (err) {
reject(err)
return
}
resolve(true)
console.log(`Database client ${this.label} connected to database`);
this.database = this.client.db(process.env.MONGO_DB_NAME)
})
})
}
/**
* Reconnects to the item database
*/
reconnect() {
return new Promise((resolve, reject) => {
this.databse = null
this.initialize.then(resolve)
})
}
createDBInfo(userID) {
return new Promise((resolve, reject) => {
if (!this.database || !this.database.collection('users')) {
reject('Error: Database not found.')
return
}
const defaultInfo = {
userID,
balance: 0,
lastClaim: -1000000000000,
voteStreak: 0,
amountDonated: 0.001,
unlockedGames: [],
unlockedItems: [],
created: Date.now(),
coins: 0,
wins: [],
achievements: [],
quests: [],
lastQuest: -1000000000000
}
this.database.collection('users').findOne({
userID
}).then(async user => {
if (!user) {
await this.database.collection('users').insertOne(defaultInfo)
resolve(defaultInfo)
} else {
resolve(user)
}
}).catch(error => console.error)
})
}
fetchDBInfo(userID) {
return new Promise(async (resolve, reject) => {
await this.createDBInfo(userID)
await this.database.collection('users').findOne({
userID
})
.then(resolve)
.catch(reject)
})
}
fetchItemInfo(itemID) {
return new Promise(async (resolve, reject) => {
await this.database.collection('items').findOne({
itemID
})
.then(resolve)
.catch(reject)
})
}
/**
* Sees if a user has an item in their inventory.
* @returns {Boolean}
*/
async hasItem (userID, itemID) {
let isItemUnlocked = false
await this.fetchDBInfo(userID)
.then(info => {
if(info && info.unlockedItems && info.unlockedItems.find(item => item == itemID)) {
isItemUnlocked = true
}
})
.catch(err => {
console.error(err)
})
return isItemUnlocked
}
async getItems() {
const collection = this.database.collection('items')
return await collection.find('*').toArray()
}
async getShopStatus () {
return new Promise(async (resolve, reject) => {
const collection = this.database.collection('status')
await collection.findOne({
type: 'shop'
}).then(resolve)
.catch(reject)
})
}
/**
* Updates a shop item
* @param item {string} The ID of the item
* @param props {object} a key-value pairing of the properties to update
* @example
* dbClient.updateShopItem('gord_board', {cost: 120000, description, 'A board that is not overpriced!'} )
*/
async updateShopItem(item, update) {
return new Promise(async (resolve, reject) => {
const collection = this.database.collection('items')
await collection.findOne({
itemID: item
}).then(resolve)
.catch(reject)
})
}
/**
* Buys a shop item
* @param userID {string} The ID of the user
* @param itemID {string} The ID of the item
* @param cost {string} The cost of the item
* @example
* dbClient.updateShopItem('gord_board', {cost: 120000, description, 'A board that is not overpriced!'} )
*/
async buyShopItem(userID, itemID, cost) {
return new Promise(async (resolve, reject) => {
const collection = this.database.collection('users')
let response = await collection.findOneAndUpdate(
{ userID },
{
$push: { unlockedItems: itemID },
$inc: { balance: -cost }
},
{ returnOriginal: false }
).catch(reject)
resolve(response)
})
}
}