sails-mongo-cloud
Version:
Mongo DB adapter for Sails.js/Waterline. Forked from sails-mongo and updated with Mongodb Driver v4.9.1
261 lines (228 loc) • 12.8 kB
JavaScript
/**
* Module dependencies
*/
var assert = require('assert');
var util = require('util');
var _ = require('@sailshq/lodash');
/**
* doWithConnection()
*
* Run the provided `during` function, passing it the provided connection.
* Or if no connection was provided, get one from the manager (and then
* release it afterwards automatically.)
*
* > Taken directly from the following code (with only very minimal modifications):
* > https://github.com/balderdashy/sails-hook-orm/blob/1cdb652aea41e2ede2305caef0d0167b79c5c052/lib/datastore-method-utils/private/do-with-connection.js
*
* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
* @param {Dictionary} options
* @required {Ref} WET_MACHINES
* @required
* @either {Ref} manager
* @or {Ref} connection
*
* @required {Function} during
* @param {Ref} db [The database connection.]
* @param {Function} proceed
* @param {Error?} err
* @param {Ref?} resultMaybe
*
* @optional {Dictionary} meta
* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
* @param {Function} done
* @param {Error?} err
* @param {Ref?} resultMaybe
* If set, this is the result sent back from the provided
* `during` function.
*/
module.exports = function doWithConnection(options, done){
assert(options.WET_MACHINES);
assert(options.manager || options.connection);
assert(_.isFunction(options.during));
assert(!options.meta || options.meta && _.isObject(options.meta));
// ╔═╗╔═╗╔═╗ ╦ ╦╦╦═╗╔═╗ ┌─┐ ┌┬┐┌┐ ┌─┐┌─┐┌┐┌┌┐┌┌─┐┌─┐┌┬┐┬┌─┐┌┐┌
// ╠═╣║ ║═╬╗║ ║║╠╦╝║╣ ├─┤ ││├┴┐ │ │ │││││││├┤ │ │ ││ ││││
// ╩ ╩╚═╝╚═╝╚╚═╝╩╩╚═╚═╝ ┴ ┴ ─┴┘└─┘ └─┘└─┘┘└┘┘└┘└─┘└─┘ ┴ ┴└─┘┘└┘
// ┬ ┬┌─┐┬┌┐┌┌─┐ ┌┬┐┬ ┬┌─┐ ┌┬┐┬─┐┬┬ ┬┌─┐┬─┐
// │ │└─┐│││││ ┬ │ ├─┤├┤ ││├┬┘│└┐┌┘├┤ ├┬┘
// └─┘└─┘┴┘└┘└─┘ ┴ ┴ ┴└─┘ ─┴┘┴└─┴ └┘ └─┘┴└─
// If a pre-leased connection was passed in, proceed with that.
// Otherwise, use the pre-built machines (i.e. from the adapter/driver)
// to acquire a new connection from the manager.
(function _ensureConnection(proceed){
if (options.connection) {
return proceed(undefined, options.connection);
}//-•
if (options.WET_MACHINES.getConnection.sync) {
var connection;
try {
connection = options.WET_MACHINES.getConnection({ manager: options.manager }).execSync().connection;
// (`report.meta` is ignored...)
} catch (e) {
if (e.exit === 'failed') {
var failureReport = e.output;
if (failureReport.meta) { failureReport.error.meta = failureReport.meta; }
return proceed(failureReport.error);
}
else { return proceed(e); }
}
return proceed(undefined, connection);
}//-•
options.WET_MACHINES.getConnection({
manager: options.manager,
meta: options.meta
}).switch({
error: function (err){ return proceed(err); },
failed: function (report){
if (report.meta) { report.error.meta = report.meta; }
return proceed(report.error);
},
success: function (report){
// (`report.meta` is ignored)
return proceed(undefined, report.connection);
}
});
})(function (err, db){
if (err) { return done(err); }
// ╦═╗╦ ╦╔╗╔ ┌┬┐┬ ┬┌─┐ \│/┌┬┐┬ ┬┬─┐┬┌┐┌┌─┐\│/ ┌─┐┬ ┬┌┐┌┌─┐┌┬┐┬┌─┐┌┐┌
// ╠╦╝║ ║║║║ │ ├─┤├┤ ─ ─ │││ │├┬┘│││││ ┬─ ─ ├┤ │ │││││ │ ││ ││││
// ╩╚═╚═╝╝╚╝ ┴ ┴ ┴└─┘ /│\─┴┘└─┘┴└─┴┘└┘└─┘/│\ └ └─┘┘└┘└─┘ ┴ ┴└─┘┘└┘
// Call the provided `during` function.
(function _makeCallToDuringFn(proceed){
// Note that, if you try to call the callback more than once in the iteratee,
// this method logs a warning explaining what's up, ignoring any subsequent calls
// to the callback that occur after the first one.
var didDuringFnAlreadyHalt;
try {
options.during(db, function (err, resultMaybe) {
if (err) { return proceed(err); }
if (didDuringFnAlreadyHalt) {
console.warn(
'Warning: The provided `during` function triggered its callback again-- after\n'+
'already triggering it once! Please carefully check your `during` function\'s \n'+
'code to figure out why this is happening. (Ignoring this subsequent invocation...)'
);
return;
}//-•
didDuringFnAlreadyHalt = true;
return proceed(undefined, resultMaybe);
});//</ invoked `during` >
} catch (e) { return proceed(e); }
})(function (duringErr, resultMaybe){
// ╦ ╦╔═╗╔╗╔╔╦╗╦ ╔═╗ ┌─┐┬─┐┬─┐┌─┐┬─┐ ┌─┐┬─┐┌─┐┌┬┐ \│/┌┬┐┬ ┬┬─┐┬┌┐┌┌─┐\│/
// ╠═╣╠═╣║║║ ║║║ ║╣ ├┤ ├┬┘├┬┘│ │├┬┘ ├┤ ├┬┘│ ││││ ─ ─ │││ │├┬┘│││││ ┬─ ─
// ╩ ╩╩ ╩╝╚╝═╩╝╩═╝╚═╝ └─┘┴└─┴└─└─┘┴└─ └ ┴└─└─┘┴ ┴ /│\─┴┘└─┘┴└─┴┘└┘└─┘/│\
// ┬ ┬─┐┌─┐┬ ┌─┐┌─┐┌─┐┌─┐ ┌─┐┌─┐┌┐┌┌┐┌┌─┐┌─┐┌┬┐┬┌─┐┌┐┌
// ┌┼─ ├┬┘├┤ │ ├┤ ├─┤└─┐├┤ │ │ │││││││├┤ │ │ ││ ││││
// └┘ ┴└─└─┘┴─┘└─┘┴ ┴└─┘└─┘ └─┘└─┘┘└┘┘└┘└─┘└─┘ ┴ ┴└─┘┘└┘
if (duringErr) {
// Since this `duringErr` came from the userland `during` fn, we can't
// completely trust it. So check it out, and if it's not one already,
// convert `duringErr` into Error instance.
if (!_.isError(duringErr)) {
// If this is a MongoError instance, transform it into a regular Error
// because that's what Waterline knows how to handle. We'll do this
// by copying all of `duringErr`s properties into a new Error.
if (_.isObject(duringErr) && duringErr.name === 'MongoError') {
duringErr = (function() {
var newError = new Error();
_.each(Object.getOwnPropertyNames(duringErr), function(prop) {
newError[prop] = duringErr[prop];
});
return newError;
})();
}
else if (_.isString(duringErr)) {
duringErr = new Error(duringErr);
}
else {
duringErr = new Error(util.inspect(duringErr, {depth:5}));
}
}//>-
// Before exiting with this `during` error, check to see if we acquired
// our own ad hoc connection earlier. If not, then go ahead and just
// send back the `during` error. But otherwise if so, then release our
// ad hoc connection first before calling the `done` callback.
if (options.connection) {
return done(duringErr);
}//-•
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
// > NOTE: We don't bother with the "is sync?" optimization here (see below).
// > Since this is already an edge case, a minor performance improvement like
// > that would be *very* low impact. As far as I see it, I can't justify
// > complicating things any further for negligible benefit. But if this is
// > a bottleneck for anyone, let me know!
// > -@mikermcneil
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
options.WET_MACHINES.releaseConnection({ connection: db, meta: options.meta }).switch({
error: function(secondaryErr) {
// This is a rare case, but still, if it happens, we make sure to tell
// the calling code _exactly_ what occurred.
return done(new Error(
'The code using this db connection encountered an error:\n'+
'``` (1)\n'+
duringErr.stack +'\n'+
'```\n'+
'...AND THEN when attempting to automatically release the db connection\n'+
'(since it was leased ad hoc), there was a secondary issue:\n'+
'``` (2)\n'+
secondaryErr.stack+'\n'+
'```'
));
},
success: function(){
return done(duringErr);
}
});//_∏_ </.releaseConnection()>
return;
}//--•
// ┌─┐┌┬┐┬ ┬┌─┐┬─┐┬ ┬┬┌─┐┌─┐ ╦ ╦╔═╗╔╗╔╔╦╗╦ ╔═╗ ┌─┐┬ ┬┌─┐┌─┐┌─┐┌─┐┌─┐
// │ │ │ ├─┤├┤ ├┬┘││││└─┐├┤ ╠═╣╠═╣║║║ ║║║ ║╣ └─┐│ ││ │ ├┤ └─┐└─┐
// └─┘ ┴ ┴ ┴└─┘┴└─└┴┘┴└─┘└─┘ ╩ ╩╩ ╩╝╚╝═╩╝╩═╝╚═╝ └─┘└─┘└─┘└─┘└─┘└─┘└─┘
// ┬ ┬─┐┌─┐┬ ┌─┐┌─┐┌─┐┌─┐ ┌─┐┌─┐┌┐┌┌┐┌┌─┐┌─┐┌┬┐┬┌─┐┌┐┌
// ┌┼─ ├┬┘├┤ │ ├┤ ├─┤└─┐├┤ │ │ │││││││├┤ │ │ ││ ││││
// └┘ ┴└─└─┘┴─┘└─┘┴ ┴└─┘└─┘ └─┘└─┘┘└┘┘└┘└─┘└─┘ ┴ ┴└─┘┘└┘
// IWMIH, then the `during` function ran successfully.
// Before exiting, check to see if we acquired our own ad hoc connection earlier.
// If not, then go ahead and just send back the result from `during`.
if (options.connection) {
return done(undefined, resultMaybe);
}//-•
// But otherwise, we must have made an ad hoc connection earlier.
// So before calling the `done` callback, try to release it.
// > NOTE: The exact code for doing so differs depending on whether the function
// > is synchronous or not. (This is not strictly necessary, it's just an optimization.)
if (options.WET_MACHINES.releaseConnection.sync) {
try {
options.WET_MACHINES.releaseConnection({ connection: db, meta: options.meta }).execSync();
// (`report.meta` is ignored...)
} catch (secondaryErr) {
return done(new Error(
'The code in the provided `during` function ran successfully with this\n'+
'db connection, but afterwards, when attempting to automatically release\n'+
'the connection (since it was leased ad hoc), there was an error:\n'+
'```\n' +
secondaryErr.stack+'\n'+
'```'
));
}
return done(undefined, resultMaybe);
}//-•
options.WET_MACHINES.releaseConnection({ connection: db, meta: options.meta }).switch({
error: function(secondaryErr) {
return done(new Error(
'The code in the provided `during` function ran successfully with this\n'+
'db connection, but afterwards, when attempting to automatically release\n'+
'the connection (since it was leased ad hoc), there was an error:\n'+
'```\n' +
secondaryErr.stack+'\n'+
'```'
));
},
success: function(){
return done(undefined, resultMaybe);
}
});//</.releaseConnection()>
});//</ _makeCallToDuringFn (self-invoking function) >
});//</ _ensureConnection (self-invoking function)>
};