/**
* @file Configure MongoDB connections using the same connection pool. Uses Mongoose >4.1.0 for Promise integration
* @version 2.0
* @author Boris GBAHOUE
* @module amiwo/db/mongo
*/
// =======================================================================
// BASIC SETUP
// =======================================================================
// Load the packages we need
const debug = require('debug')('amiwo:db');
const when = require('when');
const u = require('../../util');
// Mongoose helper
const mongoose = require('mongoose');
// MongoDB module objects
const mongo = require('mongodb').MongoClient;
// Load our objects
const GenericError = require('../../error/GenericError');
// =======================================================================
// CONSTRUCTOR
// =======================================================================
/**
* @class
* @classdesc Configure MongoDB connections using the same connection pool. Uses Mongoose >4.1.0 for Promise integration
*
* @constructor
* @param {String} name : name of this connection pool; a global variable with this name will be created referencing the connection created
* @param {Object} uri : DB URI
* @param {Object} [options] : connection options
* @param {Object} [options.direct=false] : create a connection pool using Mongo direct client (not just mongoose); accessible through global variable 'name' (if options.mongoose == false) or 'name+"_direct"' (in any case)
* @param {Object} [options.mongoose=true] : create a connection pool using Mongoose helper; accessible through global variable 'name'
*/
function ConnectionPool(name, uri, options) {
mongoose.Promise = when.Promise;
this.uri = uri;
this.name = name;
let $options = u.clone(options);
// Options processing
if ($options == null) $options = {};
// - options.direct
$options.direct = (""+$options.direct === "true") ? true : false; // null -> false
// - options.mongoose
$options.mongoose = (""+$options.mongoose === "false") ? false : true; // null -> true
this.options = $options;
this.connection = {};
if ($options.mongoose) {
this.connection.mongoose = mongoose.createConnection();
global[name] = this.connection.mongoose;
}
// Will create the direct connection later
};
// =======================================================================
// PUBLIC METHODS
// =======================================================================
/**
* Open the connection to the session database.
* Since v2.0 Creates both a Mongo (direct driver) and Mongoose connection pool (Mongoose by default, Mongo upon request)
*
* @returns {Promise}
* @method
*/
ConnectionPool.prototype.connect = function(delay) {
const self = this;
debug("::AMIWO::MONGO::CONNECTIONPOOL::%s::CONNECT::LOG Connecting to %s", self.name, self.uri);
// Open connection
const promiseArray = [];
// Mongoose
if (self.options.mongoose) {
promiseArray.push(
self.connection.mongoose.open(self.uri, self.options)
.then(function(ok) {
debug('::AMIWO::MONGO::CONNECTIONPOOL::%s::LOG Connected to Mongo database through Mongoose', self.name);
return when.resolve(true);
}).catch(function(err) {
const errorMessage = 'Error Connecting to Mongo ' + self.name + ' database through Mongoose';
debug("::AMIWO::MONGO::CONNECTIONPOOL::%s::ERROR %s => err=%s, trace=%s", self.name, errorMessage, err, err.stack);
return when.reject(new GenericError(errorMessage, err));
})
);
}
if (self.options.direct) {
promiseArray.push(
mongo.connect(self.uri, self.options)
.then(function(db) {
debug('::AMIWO::MONGO::CONNECTIONPOOL::%s::LOG Connected to Mongo database through direct driver', self.name);
self.connection.direct = db;
global[self.name+"_direct"] = self.connection.direct;
if (self.options.mongoose == false) global[self.name] = self.connection.direct;
return when.resolve(true);
}).catch(function(err) {
const errorMessage = 'Error Connecting to Mongo ' + self.name + ' database through direct driver';
debug("::AMIWO::MONGO::CONNECTIONPOOL::%s::ERROR %s => err=%s, trace=%s", self.name, errorMessage, err, err.stack);
return when.reject(new GenericError(errorMessage, err));
})
);
}
return when.all(promiseArray)
.then(function(results) {
// Register listeners to close the connection on exit / crashes
process.on('exit', $exitHandler.bind(self, "exit"));
process.on('SIGINT', $exitHandler.bind(self, "SIGINT"));
process.on('uncaughtException', $exitHandler.bind(self, "uncaughtException"));
}).catch(function(err) {
// Pass it on
return when.reject(err);
})
};
// =======================================================================
// PRIVATE METHODS
// =======================================================================
/**
* Close the connection to the MongoDB
*
* @memberOf ConnectionPool
* @private
*/
function $exitHandler(source) {
if (this.connection) {
debug("::AMIWO::MONGO::CONNECTIONPOOL::%s::EXITHANDLER::%s::LOG Closing connection to %s", this.name, source, this.uri);
if (this.connection.mongoose) this.connection.mongoose.close();
if (this.connection.direct) this.connection.direct.close();
this.connection = null;
}
}
module.exports = ConnectionPool;