database and ajax test added
This commit is contained in:
+62
@@ -0,0 +1,62 @@
|
||||
'use strict';
|
||||
|
||||
const executeCommand = require('./db_ops').executeCommand;
|
||||
const executeDbAdminCommand = require('./db_ops').executeDbAdminCommand;
|
||||
|
||||
/**
|
||||
* Get ReplicaSet status
|
||||
*
|
||||
* @param {Admin} a collection instance.
|
||||
* @param {Object} [options] Optional settings. See Admin.prototype.replSetGetStatus for a list of options.
|
||||
* @param {Admin~resultCallback} [callback] The command result callback.
|
||||
*/
|
||||
function replSetGetStatus(admin, options, callback) {
|
||||
executeDbAdminCommand(admin.s.db, { replSetGetStatus: 1 }, options, callback);
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve this db's server status.
|
||||
*
|
||||
* @param {Admin} a collection instance.
|
||||
* @param {Object} [options] Optional settings. See Admin.prototype.serverStatus for a list of options.
|
||||
* @param {Admin~resultCallback} [callback] The command result callback
|
||||
*/
|
||||
function serverStatus(admin, options, callback) {
|
||||
executeDbAdminCommand(admin.s.db, { serverStatus: 1 }, options, callback);
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate an existing collection
|
||||
*
|
||||
* @param {Admin} a collection instance.
|
||||
* @param {string} collectionName The name of the collection to validate.
|
||||
* @param {Object} [options] Optional settings. See Admin.prototype.validateCollection for a list of options.
|
||||
* @param {Admin~resultCallback} [callback] The command result callback.
|
||||
*/
|
||||
function validateCollection(admin, collectionName, options, callback) {
|
||||
const command = { validate: collectionName };
|
||||
const keys = Object.keys(options);
|
||||
|
||||
// Decorate command with extra options
|
||||
for (let i = 0; i < keys.length; i++) {
|
||||
if (options.hasOwnProperty(keys[i]) && keys[i] !== 'session') {
|
||||
command[keys[i]] = options[keys[i]];
|
||||
}
|
||||
}
|
||||
|
||||
executeCommand(admin.s.db, command, options, (err, doc) => {
|
||||
if (err != null) return callback(err, null);
|
||||
|
||||
if (doc.ok === 0) return callback(new Error('Error with validate command'), null);
|
||||
if (doc.result != null && doc.result.constructor !== String)
|
||||
return callback(new Error('Error with validation data'), null);
|
||||
if (doc.result != null && doc.result.match(/exception|corrupt/) != null)
|
||||
return callback(new Error('Error: invalid collection ' + collectionName), null);
|
||||
if (doc.valid != null && !doc.valid)
|
||||
return callback(new Error('Error: invalid collection ' + collectionName), null);
|
||||
|
||||
return callback(null, doc);
|
||||
});
|
||||
}
|
||||
|
||||
module.exports = { replSetGetStatus, serverStatus, validateCollection };
|
||||
+1479
File diff suppressed because it is too large
Load Diff
+242
@@ -0,0 +1,242 @@
|
||||
'use strict';
|
||||
|
||||
const buildCountCommand = require('./collection_ops').buildCountCommand;
|
||||
const formattedOrderClause = require('../utils').formattedOrderClause;
|
||||
const handleCallback = require('../utils').handleCallback;
|
||||
const MongoError = require('mongodb-core').MongoError;
|
||||
const push = Array.prototype.push;
|
||||
|
||||
/**
|
||||
* Get the count of documents for this cursor.
|
||||
*
|
||||
* @method
|
||||
* @param {Cursor} cursor The Cursor instance on which to count.
|
||||
* @param {boolean} [applySkipLimit=true] Specifies whether the count command apply limit and skip settings should be applied on the cursor or in the provided options.
|
||||
* @param {object} [options] Optional settings. See Cursor.prototype.count for a list of options.
|
||||
* @param {Cursor~countResultCallback} [callback] The result callback.
|
||||
*/
|
||||
function count(cursor, applySkipLimit, opts, callback) {
|
||||
if (applySkipLimit) {
|
||||
if (typeof cursor.cursorSkip() === 'number') opts.skip = cursor.cursorSkip();
|
||||
if (typeof cursor.cursorLimit() === 'number') opts.limit = cursor.cursorLimit();
|
||||
}
|
||||
|
||||
// Ensure we have the right read preference inheritance
|
||||
if (opts.readPreference) {
|
||||
cursor.setReadPreference(opts.readPreference);
|
||||
}
|
||||
|
||||
if (
|
||||
typeof opts.maxTimeMS !== 'number' &&
|
||||
cursor.s.cmd &&
|
||||
typeof cursor.s.cmd.maxTimeMS === 'number'
|
||||
) {
|
||||
opts.maxTimeMS = cursor.s.cmd.maxTimeMS;
|
||||
}
|
||||
|
||||
let options = {};
|
||||
options.skip = opts.skip;
|
||||
options.limit = opts.limit;
|
||||
options.hint = opts.hint;
|
||||
options.maxTimeMS = opts.maxTimeMS;
|
||||
|
||||
// Command
|
||||
const delimiter = cursor.s.ns.indexOf('.');
|
||||
options.collectionName = cursor.s.ns.substr(delimiter + 1);
|
||||
|
||||
let command;
|
||||
try {
|
||||
command = buildCountCommand(cursor, cursor.s.cmd.query, options);
|
||||
} catch (err) {
|
||||
return callback(err);
|
||||
}
|
||||
|
||||
// Set cursor server to the same as the topology
|
||||
cursor.server = cursor.topology.s.coreTopology;
|
||||
|
||||
// Execute the command
|
||||
cursor.s.topology.command(
|
||||
`${cursor.s.ns.substr(0, delimiter)}.$cmd`,
|
||||
command,
|
||||
cursor.s.options,
|
||||
(err, result) => {
|
||||
callback(err, result ? result.result.n : null);
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Iterates over all the documents for this cursor. See Cursor.prototype.each for more information.
|
||||
*
|
||||
* @method
|
||||
* @deprecated
|
||||
* @param {Cursor} cursor The Cursor instance on which to run.
|
||||
* @param {Cursor~resultCallback} callback The result callback.
|
||||
*/
|
||||
function each(cursor, callback) {
|
||||
const Cursor = require('../cursor');
|
||||
|
||||
if (!callback) throw MongoError.create({ message: 'callback is mandatory', driver: true });
|
||||
if (cursor.isNotified()) return;
|
||||
if (cursor.s.state === Cursor.CLOSED || cursor.isDead()) {
|
||||
return handleCallback(
|
||||
callback,
|
||||
MongoError.create({ message: 'Cursor is closed', driver: true })
|
||||
);
|
||||
}
|
||||
|
||||
if (cursor.s.state === Cursor.INIT) cursor.s.state = Cursor.OPEN;
|
||||
|
||||
// Define function to avoid global scope escape
|
||||
let fn = null;
|
||||
// Trampoline all the entries
|
||||
if (cursor.bufferedCount() > 0) {
|
||||
while ((fn = loop(cursor, callback))) fn(cursor, callback);
|
||||
each(cursor, callback);
|
||||
} else {
|
||||
cursor.next((err, item) => {
|
||||
if (err) return handleCallback(callback, err);
|
||||
if (item == null) {
|
||||
return cursor.close({ skipKillCursors: true }, () => handleCallback(callback, null, null));
|
||||
}
|
||||
|
||||
if (handleCallback(callback, null, item) === false) return;
|
||||
each(cursor, callback);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if there is any document still available in the cursor.
|
||||
*
|
||||
* @method
|
||||
* @param {Cursor} cursor The Cursor instance on which to run.
|
||||
* @param {Cursor~resultCallback} [callback] The result callback.
|
||||
*/
|
||||
function hasNext(cursor, callback) {
|
||||
const Cursor = require('../cursor');
|
||||
|
||||
if (cursor.s.currentDoc) {
|
||||
return callback(null, true);
|
||||
}
|
||||
|
||||
if (cursor.isNotified()) {
|
||||
return callback(null, false);
|
||||
}
|
||||
|
||||
nextObject(cursor, (err, doc) => {
|
||||
if (err) return callback(err, null);
|
||||
if (cursor.s.state === Cursor.CLOSED || cursor.isDead()) return callback(null, false);
|
||||
if (!doc) return callback(null, false);
|
||||
cursor.s.currentDoc = doc;
|
||||
callback(null, true);
|
||||
});
|
||||
}
|
||||
|
||||
// Trampoline emptying the number of retrieved items
|
||||
// without incurring a nextTick operation
|
||||
function loop(cursor, callback) {
|
||||
// No more items we are done
|
||||
if (cursor.bufferedCount() === 0) return;
|
||||
// Get the next document
|
||||
cursor._next(callback);
|
||||
// Loop
|
||||
return loop;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the next available document from the cursor. Returns null if no more documents are available.
|
||||
*
|
||||
* @method
|
||||
* @param {Cursor} cursor The Cursor instance from which to get the next document.
|
||||
* @param {Cursor~resultCallback} [callback] The result callback.
|
||||
*/
|
||||
function next(cursor, callback) {
|
||||
// Return the currentDoc if someone called hasNext first
|
||||
if (cursor.s.currentDoc) {
|
||||
const doc = cursor.s.currentDoc;
|
||||
cursor.s.currentDoc = null;
|
||||
return callback(null, doc);
|
||||
}
|
||||
|
||||
// Return the next object
|
||||
nextObject(cursor, callback);
|
||||
}
|
||||
|
||||
// Get the next available document from the cursor, returns null if no more documents are available.
|
||||
function nextObject(cursor, callback) {
|
||||
const Cursor = require('../cursor');
|
||||
|
||||
if (cursor.s.state === Cursor.CLOSED || (cursor.isDead && cursor.isDead()))
|
||||
return handleCallback(
|
||||
callback,
|
||||
MongoError.create({ message: 'Cursor is closed', driver: true })
|
||||
);
|
||||
if (cursor.s.state === Cursor.INIT && cursor.s.cmd.sort) {
|
||||
try {
|
||||
cursor.s.cmd.sort = formattedOrderClause(cursor.s.cmd.sort);
|
||||
} catch (err) {
|
||||
return handleCallback(callback, err);
|
||||
}
|
||||
}
|
||||
|
||||
// Get the next object
|
||||
cursor._next((err, doc) => {
|
||||
cursor.s.state = Cursor.OPEN;
|
||||
if (err) return handleCallback(callback, err);
|
||||
handleCallback(callback, null, doc);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an array of documents. See Cursor.prototype.toArray for more information.
|
||||
*
|
||||
* @method
|
||||
* @param {Cursor} cursor The Cursor instance from which to get the next document.
|
||||
* @param {Cursor~toArrayResultCallback} [callback] The result callback.
|
||||
*/
|
||||
function toArray(cursor, callback) {
|
||||
const Cursor = require('../cursor');
|
||||
|
||||
const items = [];
|
||||
|
||||
// Reset cursor
|
||||
cursor.rewind();
|
||||
cursor.s.state = Cursor.INIT;
|
||||
|
||||
// Fetch all the documents
|
||||
const fetchDocs = () => {
|
||||
cursor._next((err, doc) => {
|
||||
if (err) {
|
||||
return cursor._endSession
|
||||
? cursor._endSession(() => handleCallback(callback, err))
|
||||
: handleCallback(callback, err);
|
||||
}
|
||||
if (doc == null) {
|
||||
return cursor.close({ skipKillCursors: true }, () => handleCallback(callback, null, items));
|
||||
}
|
||||
|
||||
// Add doc to items
|
||||
items.push(doc);
|
||||
|
||||
// Get all buffered objects
|
||||
if (cursor.bufferedCount() > 0) {
|
||||
let docs = cursor.readBufferedDocuments(cursor.bufferedCount());
|
||||
|
||||
// Transform the doc if transform method added
|
||||
if (cursor.s.transforms && typeof cursor.s.transforms.doc === 'function') {
|
||||
docs = docs.map(cursor.s.transforms.doc);
|
||||
}
|
||||
|
||||
push.apply(items, docs);
|
||||
}
|
||||
|
||||
// Attempt a fetch
|
||||
fetchDocs();
|
||||
});
|
||||
};
|
||||
|
||||
fetchDocs();
|
||||
}
|
||||
|
||||
module.exports = { count, each, hasNext, next, toArray };
|
||||
+986
@@ -0,0 +1,986 @@
|
||||
'use strict';
|
||||
|
||||
const applyWriteConcern = require('../utils').applyWriteConcern;
|
||||
const Code = require('mongodb-core').BSON.Code;
|
||||
const resolveReadPreference = require('../utils').resolveReadPreference;
|
||||
const crypto = require('crypto');
|
||||
const debugOptions = require('../utils').debugOptions;
|
||||
const handleCallback = require('../utils').handleCallback;
|
||||
const MongoError = require('mongodb-core').MongoError;
|
||||
const parseIndexOptions = require('../utils').parseIndexOptions;
|
||||
const ReadPreference = require('mongodb-core').ReadPreference;
|
||||
const toError = require('../utils').toError;
|
||||
const CONSTANTS = require('../constants');
|
||||
|
||||
const count = require('./collection_ops').count;
|
||||
const findOne = require('./collection_ops').findOne;
|
||||
const remove = require('./collection_ops').remove;
|
||||
const updateOne = require('./collection_ops').updateOne;
|
||||
|
||||
const debugFields = [
|
||||
'authSource',
|
||||
'w',
|
||||
'wtimeout',
|
||||
'j',
|
||||
'native_parser',
|
||||
'forceServerObjectId',
|
||||
'serializeFunctions',
|
||||
'raw',
|
||||
'promoteLongs',
|
||||
'promoteValues',
|
||||
'promoteBuffers',
|
||||
'bufferMaxEntries',
|
||||
'numberOfRetries',
|
||||
'retryMiliSeconds',
|
||||
'readPreference',
|
||||
'pkFactory',
|
||||
'parentDb',
|
||||
'promiseLibrary',
|
||||
'noListener'
|
||||
];
|
||||
|
||||
// Filter out any write concern options
|
||||
const illegalCommandFields = [
|
||||
'w',
|
||||
'wtimeout',
|
||||
'j',
|
||||
'fsync',
|
||||
'autoIndexId',
|
||||
'strict',
|
||||
'serializeFunctions',
|
||||
'pkFactory',
|
||||
'raw',
|
||||
'readPreference',
|
||||
'session'
|
||||
];
|
||||
|
||||
/**
|
||||
* Add a user to the database.
|
||||
* @method
|
||||
* @param {Db} db The Db instance on which to add a user.
|
||||
* @param {string} username The username.
|
||||
* @param {string} password The password.
|
||||
* @param {object} [options] Optional settings. See Db.prototype.addUser for a list of options.
|
||||
* @param {Db~resultCallback} [callback] The command result callback
|
||||
*/
|
||||
function addUser(db, username, password, options, callback) {
|
||||
const Db = require('../db');
|
||||
|
||||
// Did the user destroy the topology
|
||||
if (db.serverConfig && db.serverConfig.isDestroyed())
|
||||
return callback(new MongoError('topology was destroyed'));
|
||||
// Attempt to execute auth command
|
||||
executeAuthCreateUserCommand(db, username, password, options, (err, r) => {
|
||||
// We need to perform the backward compatible insert operation
|
||||
if (err && err.code === -5000) {
|
||||
const finalOptions = applyWriteConcern(Object.assign({}, options), { db }, options);
|
||||
|
||||
// Use node md5 generator
|
||||
const md5 = crypto.createHash('md5');
|
||||
// Generate keys used for authentication
|
||||
md5.update(username + ':mongo:' + password);
|
||||
const userPassword = md5.digest('hex');
|
||||
|
||||
// If we have another db set
|
||||
const db = options.dbName ? new Db(options.dbName, db.s.topology, db.s.options) : db;
|
||||
|
||||
// Fetch a user collection
|
||||
const collection = db.collection(CONSTANTS.SYSTEM_USER_COLLECTION);
|
||||
|
||||
// Check if we are inserting the first user
|
||||
count(collection, {}, finalOptions, (err, count) => {
|
||||
// We got an error (f.ex not authorized)
|
||||
if (err != null) return handleCallback(callback, err, null);
|
||||
// Check if the user exists and update i
|
||||
const findOptions = Object.assign({ projection: { dbName: 1 } }, finalOptions);
|
||||
collection.find({ user: username }, findOptions).toArray(err => {
|
||||
// We got an error (f.ex not authorized)
|
||||
if (err != null) return handleCallback(callback, err, null);
|
||||
// Add command keys
|
||||
finalOptions.upsert = true;
|
||||
|
||||
// We have a user, let's update the password or upsert if not
|
||||
updateOne(
|
||||
collection,
|
||||
{ user: username },
|
||||
{ $set: { user: username, pwd: userPassword } },
|
||||
finalOptions,
|
||||
err => {
|
||||
if (count === 0 && err)
|
||||
return handleCallback(callback, null, [{ user: username, pwd: userPassword }]);
|
||||
if (err) return handleCallback(callback, err, null);
|
||||
handleCallback(callback, null, [{ user: username, pwd: userPassword }]);
|
||||
}
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (err) return handleCallback(callback, err);
|
||||
handleCallback(callback, err, r);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetch all collections for the current db.
|
||||
*
|
||||
* @method
|
||||
* @param {Db} db The Db instance on which to fetch collections.
|
||||
* @param {object} [options] Optional settings. See Db.prototype.collections for a list of options.
|
||||
* @param {Db~collectionsResultCallback} [callback] The results callback
|
||||
*/
|
||||
function collections(db, options, callback) {
|
||||
const Collection = require('../collection');
|
||||
|
||||
options = Object.assign({}, options, { nameOnly: true });
|
||||
// Let's get the collection names
|
||||
db.listCollections({}, options).toArray((err, documents) => {
|
||||
if (err != null) return handleCallback(callback, err, null);
|
||||
// Filter collections removing any illegal ones
|
||||
documents = documents.filter(doc => {
|
||||
return doc.name.indexOf('$') === -1;
|
||||
});
|
||||
|
||||
// Return the collection objects
|
||||
handleCallback(
|
||||
callback,
|
||||
null,
|
||||
documents.map(d => {
|
||||
return new Collection(
|
||||
db,
|
||||
db.s.topology,
|
||||
db.s.databaseName,
|
||||
d.name,
|
||||
db.s.pkFactory,
|
||||
db.s.options
|
||||
);
|
||||
})
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new collection on a server with the specified options. Use this to create capped collections.
|
||||
* More information about command options available at https://docs.mongodb.com/manual/reference/command/create/
|
||||
*
|
||||
* @method
|
||||
* @param {Db} db The Db instance on which to create the collection.
|
||||
* @param {string} name The collection name to create.
|
||||
* @param {object} [options] Optional settings. See Db.prototype.createCollection for a list of options.
|
||||
* @param {Db~collectionResultCallback} [callback] The results callback
|
||||
*/
|
||||
function createCollection(db, name, options, callback) {
|
||||
const Collection = require('../collection');
|
||||
|
||||
// Get the write concern options
|
||||
const finalOptions = applyWriteConcern(Object.assign({}, options), { db }, options);
|
||||
|
||||
// Did the user destroy the topology
|
||||
if (db.serverConfig && db.serverConfig.isDestroyed()) {
|
||||
return callback(new MongoError('topology was destroyed'));
|
||||
}
|
||||
|
||||
const listCollectionOptions = Object.assign({}, finalOptions, { nameOnly: true });
|
||||
|
||||
// Check if we have the name
|
||||
db
|
||||
.listCollections({ name }, listCollectionOptions)
|
||||
.setReadPreference(ReadPreference.PRIMARY)
|
||||
.toArray((err, collections) => {
|
||||
if (err != null) return handleCallback(callback, err, null);
|
||||
if (collections.length > 0 && finalOptions.strict) {
|
||||
return handleCallback(
|
||||
callback,
|
||||
MongoError.create({
|
||||
message: `Collection ${name} already exists. Currently in strict mode.`,
|
||||
driver: true
|
||||
}),
|
||||
null
|
||||
);
|
||||
} else if (collections.length > 0) {
|
||||
try {
|
||||
return handleCallback(
|
||||
callback,
|
||||
null,
|
||||
new Collection(db, db.s.topology, db.s.databaseName, name, db.s.pkFactory, options)
|
||||
);
|
||||
} catch (err) {
|
||||
return handleCallback(callback, err);
|
||||
}
|
||||
}
|
||||
|
||||
// Create collection command
|
||||
const cmd = { create: name };
|
||||
|
||||
// Decorate command with writeConcern if supported
|
||||
applyWriteConcern(cmd, { db }, options);
|
||||
|
||||
// Add all optional parameters
|
||||
for (let n in options) {
|
||||
if (
|
||||
options[n] != null &&
|
||||
typeof options[n] !== 'function' &&
|
||||
illegalCommandFields.indexOf(n) === -1
|
||||
) {
|
||||
cmd[n] = options[n];
|
||||
}
|
||||
}
|
||||
|
||||
// Force a primary read Preference
|
||||
finalOptions.readPreference = ReadPreference.PRIMARY;
|
||||
// Execute command
|
||||
executeCommand(db, cmd, finalOptions, err => {
|
||||
if (err) return handleCallback(callback, err);
|
||||
handleCallback(
|
||||
callback,
|
||||
null,
|
||||
new Collection(db, db.s.topology, db.s.databaseName, name, db.s.pkFactory, options)
|
||||
);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates an index on the db and collection.
|
||||
* @method
|
||||
* @param {Db} db The Db instance on which to create an index.
|
||||
* @param {string} name Name of the collection to create the index on.
|
||||
* @param {(string|object)} fieldOrSpec Defines the index.
|
||||
* @param {object} [options] Optional settings. See Db.prototype.createIndex for a list of options.
|
||||
* @param {Db~resultCallback} [callback] The command result callback
|
||||
*/
|
||||
function createIndex(db, name, fieldOrSpec, options, callback) {
|
||||
// Get the write concern options
|
||||
let finalOptions = Object.assign({}, { readPreference: ReadPreference.PRIMARY }, options);
|
||||
finalOptions = applyWriteConcern(finalOptions, { db }, options);
|
||||
|
||||
// Ensure we have a callback
|
||||
if (finalOptions.writeConcern && typeof callback !== 'function') {
|
||||
throw MongoError.create({
|
||||
message: 'Cannot use a writeConcern without a provided callback',
|
||||
driver: true
|
||||
});
|
||||
}
|
||||
|
||||
// Did the user destroy the topology
|
||||
if (db.serverConfig && db.serverConfig.isDestroyed())
|
||||
return callback(new MongoError('topology was destroyed'));
|
||||
|
||||
// Attempt to run using createIndexes command
|
||||
createIndexUsingCreateIndexes(db, name, fieldOrSpec, finalOptions, (err, result) => {
|
||||
if (err == null) return handleCallback(callback, err, result);
|
||||
|
||||
/**
|
||||
* The following errors mean that the server recognized `createIndex` as a command so we don't need to fallback to an insert:
|
||||
* 67 = 'CannotCreateIndex' (malformed index options)
|
||||
* 85 = 'IndexOptionsConflict' (index already exists with different options)
|
||||
* 86 = 'IndexKeySpecsConflict' (index already exists with the same name)
|
||||
* 11000 = 'DuplicateKey' (couldn't build unique index because of dupes)
|
||||
* 11600 = 'InterruptedAtShutdown' (interrupted at shutdown)
|
||||
* 197 = 'InvalidIndexSpecificationOption' (`_id` with `background: true`)
|
||||
*/
|
||||
if (
|
||||
err.code === 67 ||
|
||||
err.code === 11000 ||
|
||||
err.code === 85 ||
|
||||
err.code === 86 ||
|
||||
err.code === 11600 ||
|
||||
err.code === 197
|
||||
) {
|
||||
return handleCallback(callback, err, result);
|
||||
}
|
||||
|
||||
// Create command
|
||||
const doc = createCreateIndexCommand(db, name, fieldOrSpec, options);
|
||||
// Set no key checking
|
||||
finalOptions.checkKeys = false;
|
||||
// Insert document
|
||||
db.s.topology.insert(
|
||||
`${db.s.databaseName}.${CONSTANTS.SYSTEM_INDEX_COLLECTION}`,
|
||||
doc,
|
||||
finalOptions,
|
||||
(err, result) => {
|
||||
if (callback == null) return;
|
||||
if (err) return handleCallback(callback, err);
|
||||
if (result == null) return handleCallback(callback, null, null);
|
||||
if (result.result.writeErrors)
|
||||
return handleCallback(callback, MongoError.create(result.result.writeErrors[0]), null);
|
||||
handleCallback(callback, null, doc.name);
|
||||
}
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
// Add listeners to topology
|
||||
function createListener(db, e, object) {
|
||||
function listener(err) {
|
||||
if (object.listeners(e).length > 0) {
|
||||
object.emit(e, err, db);
|
||||
|
||||
// Emit on all associated db's if available
|
||||
for (let i = 0; i < db.s.children.length; i++) {
|
||||
db.s.children[i].emit(e, err, db.s.children[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
return listener;
|
||||
}
|
||||
|
||||
/**
|
||||
* Drop a collection from the database, removing it permanently. New accesses will create a new collection.
|
||||
*
|
||||
* @method
|
||||
* @param {Db} db The Db instance on which to drop the collection.
|
||||
* @param {string} name Name of collection to drop
|
||||
* @param {Object} [options] Optional settings. See Db.prototype.dropCollection for a list of options.
|
||||
* @param {Db~resultCallback} [callback] The results callback
|
||||
*/
|
||||
function dropCollection(db, name, options, callback) {
|
||||
executeCommand(db, name, options, (err, result) => {
|
||||
// Did the user destroy the topology
|
||||
if (db.serverConfig && db.serverConfig.isDestroyed()) {
|
||||
return callback(new MongoError('topology was destroyed'));
|
||||
}
|
||||
|
||||
if (err) return handleCallback(callback, err);
|
||||
if (result.ok) return handleCallback(callback, null, true);
|
||||
handleCallback(callback, null, false);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Drop a database, removing it permanently from the server.
|
||||
*
|
||||
* @method
|
||||
* @param {Db} db The Db instance to drop.
|
||||
* @param {Object} cmd The command document.
|
||||
* @param {Object} [options] Optional settings. See Db.prototype.dropDatabase for a list of options.
|
||||
* @param {Db~resultCallback} [callback] The results callback
|
||||
*/
|
||||
function dropDatabase(db, cmd, options, callback) {
|
||||
executeCommand(db, cmd, options, (err, result) => {
|
||||
// Did the user destroy the topology
|
||||
if (db.serverConfig && db.serverConfig.isDestroyed()) {
|
||||
return callback(new MongoError('topology was destroyed'));
|
||||
}
|
||||
|
||||
if (callback == null) return;
|
||||
if (err) return handleCallback(callback, err, null);
|
||||
handleCallback(callback, null, result.ok ? true : false);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Ensures that an index exists. If it does not, creates it.
|
||||
*
|
||||
* @method
|
||||
* @param {Db} db The Db instance on which to ensure the index.
|
||||
* @param {string} name The index name
|
||||
* @param {(string|object)} fieldOrSpec Defines the index.
|
||||
* @param {object} [options] Optional settings. See Db.prototype.ensureIndex for a list of options.
|
||||
* @param {Db~resultCallback} [callback] The command result callback
|
||||
*/
|
||||
function ensureIndex(db, name, fieldOrSpec, options, callback) {
|
||||
// Get the write concern options
|
||||
const finalOptions = applyWriteConcern({}, { db }, options);
|
||||
// Create command
|
||||
const selector = createCreateIndexCommand(db, name, fieldOrSpec, options);
|
||||
const index_name = selector.name;
|
||||
|
||||
// Did the user destroy the topology
|
||||
if (db.serverConfig && db.serverConfig.isDestroyed())
|
||||
return callback(new MongoError('topology was destroyed'));
|
||||
|
||||
// Merge primary readPreference
|
||||
finalOptions.readPreference = ReadPreference.PRIMARY;
|
||||
|
||||
// Check if the index already exists
|
||||
indexInformation(db, name, finalOptions, (err, indexInformation) => {
|
||||
if (err != null && err.code !== 26) return handleCallback(callback, err, null);
|
||||
// If the index does not exist, create it
|
||||
if (indexInformation == null || !indexInformation[index_name]) {
|
||||
createIndex(db, name, fieldOrSpec, options, callback);
|
||||
} else {
|
||||
if (typeof callback === 'function') return handleCallback(callback, null, index_name);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Evaluate JavaScript on the server
|
||||
*
|
||||
* @method
|
||||
* @param {Db} db The Db instance.
|
||||
* @param {Code} code JavaScript to execute on server.
|
||||
* @param {(object|array)} parameters The parameters for the call.
|
||||
* @param {object} [options] Optional settings. See Db.prototype.eval for a list of options.
|
||||
* @param {Db~resultCallback} [callback] The results callback
|
||||
* @deprecated Eval is deprecated on MongoDB 3.2 and forward
|
||||
*/
|
||||
function evaluate(db, code, parameters, options, callback) {
|
||||
let finalCode = code;
|
||||
let finalParameters = [];
|
||||
|
||||
// Did the user destroy the topology
|
||||
if (db.serverConfig && db.serverConfig.isDestroyed())
|
||||
return callback(new MongoError('topology was destroyed'));
|
||||
|
||||
// If not a code object translate to one
|
||||
if (!(finalCode && finalCode._bsontype === 'Code')) finalCode = new Code(finalCode);
|
||||
// Ensure the parameters are correct
|
||||
if (parameters != null && !Array.isArray(parameters) && typeof parameters !== 'function') {
|
||||
finalParameters = [parameters];
|
||||
} else if (parameters != null && Array.isArray(parameters) && typeof parameters !== 'function') {
|
||||
finalParameters = parameters;
|
||||
}
|
||||
|
||||
// Create execution selector
|
||||
let cmd = { $eval: finalCode, args: finalParameters };
|
||||
// Check if the nolock parameter is passed in
|
||||
if (options['nolock']) {
|
||||
cmd['nolock'] = options['nolock'];
|
||||
}
|
||||
|
||||
// Set primary read preference
|
||||
options.readPreference = new ReadPreference(ReadPreference.PRIMARY);
|
||||
|
||||
// Execute the command
|
||||
executeCommand(db, cmd, options, (err, result) => {
|
||||
if (err) return handleCallback(callback, err, null);
|
||||
if (result && result.ok === 1) return handleCallback(callback, null, result.retval);
|
||||
if (result)
|
||||
return handleCallback(
|
||||
callback,
|
||||
MongoError.create({ message: `eval failed: ${result.errmsg}`, driver: true }),
|
||||
null
|
||||
);
|
||||
handleCallback(callback, err, result);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute a command
|
||||
*
|
||||
* @method
|
||||
* @param {Db} db The Db instance on which to execute the command.
|
||||
* @param {object} command The command hash
|
||||
* @param {object} [options] Optional settings. See Db.prototype.command for a list of options.
|
||||
* @param {Db~resultCallback} [callback] The command result callback
|
||||
*/
|
||||
function executeCommand(db, command, options, callback) {
|
||||
// Did the user destroy the topology
|
||||
if (db.serverConfig && db.serverConfig.isDestroyed())
|
||||
return callback(new MongoError('topology was destroyed'));
|
||||
// Get the db name we are executing against
|
||||
const dbName = options.dbName || options.authdb || db.s.databaseName;
|
||||
|
||||
// Convert the readPreference if its not a write
|
||||
options.readPreference = resolveReadPreference(options, { db, default: ReadPreference.primary });
|
||||
|
||||
// Debug information
|
||||
if (db.s.logger.isDebug())
|
||||
db.s.logger.debug(
|
||||
`executing command ${JSON.stringify(
|
||||
command
|
||||
)} against ${dbName}.$cmd with options [${JSON.stringify(
|
||||
debugOptions(debugFields, options)
|
||||
)}]`
|
||||
);
|
||||
|
||||
// Execute command
|
||||
db.s.topology.command(`${dbName}.$cmd`, command, options, (err, result) => {
|
||||
if (err) return handleCallback(callback, err);
|
||||
if (options.full) return handleCallback(callback, null, result);
|
||||
handleCallback(callback, null, result.result);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Runs a command on the database as admin.
|
||||
*
|
||||
* @method
|
||||
* @param {Db} db The Db instance on which to execute the command.
|
||||
* @param {object} command The command hash
|
||||
* @param {object} [options] Optional settings. See Db.prototype.executeDbAdminCommand for a list of options.
|
||||
* @param {Db~resultCallback} [callback] The command result callback
|
||||
*/
|
||||
function executeDbAdminCommand(db, command, options, callback) {
|
||||
db.s.topology.command('admin.$cmd', command, options, (err, result) => {
|
||||
// Did the user destroy the topology
|
||||
if (db.serverConfig && db.serverConfig.isDestroyed()) {
|
||||
return callback(new MongoError('topology was destroyed'));
|
||||
}
|
||||
|
||||
if (err) return handleCallback(callback, err);
|
||||
handleCallback(callback, null, result.result);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves this collections index info.
|
||||
*
|
||||
* @method
|
||||
* @param {Db} db The Db instance on which to retrieve the index info.
|
||||
* @param {string} name The name of the collection.
|
||||
* @param {object} [options] Optional settings. See Db.prototype.indexInformation for a list of options.
|
||||
* @param {Db~resultCallback} [callback] The command result callback
|
||||
*/
|
||||
function indexInformation(db, name, options, callback) {
|
||||
// If we specified full information
|
||||
const full = options['full'] == null ? false : options['full'];
|
||||
|
||||
// Did the user destroy the topology
|
||||
if (db.serverConfig && db.serverConfig.isDestroyed())
|
||||
return callback(new MongoError('topology was destroyed'));
|
||||
// Process all the results from the index command and collection
|
||||
function processResults(indexes) {
|
||||
// Contains all the information
|
||||
let info = {};
|
||||
// Process all the indexes
|
||||
for (let i = 0; i < indexes.length; i++) {
|
||||
const index = indexes[i];
|
||||
// Let's unpack the object
|
||||
info[index.name] = [];
|
||||
for (let name in index.key) {
|
||||
info[index.name].push([name, index.key[name]]);
|
||||
}
|
||||
}
|
||||
|
||||
return info;
|
||||
}
|
||||
|
||||
// Get the list of indexes of the specified collection
|
||||
db
|
||||
.collection(name)
|
||||
.listIndexes(options)
|
||||
.toArray((err, indexes) => {
|
||||
if (err) return callback(toError(err));
|
||||
if (!Array.isArray(indexes)) return handleCallback(callback, null, []);
|
||||
if (full) return handleCallback(callback, null, indexes);
|
||||
handleCallback(callback, null, processResults(indexes));
|
||||
});
|
||||
}
|
||||
|
||||
// Transformation methods for cursor results
|
||||
function listCollectionsTransforms(databaseName) {
|
||||
const matching = `${databaseName}.`;
|
||||
|
||||
return {
|
||||
doc: doc => {
|
||||
const index = doc.name.indexOf(matching);
|
||||
// Remove database name if available
|
||||
if (doc.name && index === 0) {
|
||||
doc.name = doc.name.substr(index + matching.length);
|
||||
}
|
||||
|
||||
return doc;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrive the current profiling information for MongoDB
|
||||
*
|
||||
* @method
|
||||
* @param {Db} db The Db instance on which to retrieve the profiling info.
|
||||
* @param {Object} [options] Optional settings. See Db.protoype.profilingInfo for a list of options.
|
||||
* @param {Db~resultCallback} [callback] The command result callback.
|
||||
* @deprecated Query the system.profile collection directly.
|
||||
*/
|
||||
function profilingInfo(db, options, callback) {
|
||||
try {
|
||||
db
|
||||
.collection('system.profile')
|
||||
.find({}, options)
|
||||
.toArray(callback);
|
||||
} catch (err) {
|
||||
return callback(err, null);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve the current profiling level for MongoDB
|
||||
*
|
||||
* @method
|
||||
* @param {Db} db The Db instance on which to retrieve the profiling level.
|
||||
* @param {Object} [options] Optional settings. See Db.prototype.profilingLevel for a list of options.
|
||||
* @param {Db~resultCallback} [callback] The command result callback
|
||||
*/
|
||||
function profilingLevel(db, options, callback) {
|
||||
executeCommand(db, { profile: -1 }, options, (err, doc) => {
|
||||
if (err == null && doc.ok === 1) {
|
||||
const was = doc.was;
|
||||
if (was === 0) return callback(null, 'off');
|
||||
if (was === 1) return callback(null, 'slow_only');
|
||||
if (was === 2) return callback(null, 'all');
|
||||
return callback(new Error('Error: illegal profiling level value ' + was), null);
|
||||
} else {
|
||||
err != null ? callback(err, null) : callback(new Error('Error with profile command'), null);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove a user from a database
|
||||
*
|
||||
* @method
|
||||
* @param {Db} db The Db instance on which to remove the user.
|
||||
* @param {string} username The username.
|
||||
* @param {object} [options] Optional settings. See Db.prototype.removeUser for a list of options.
|
||||
* @param {Db~resultCallback} [callback] The command result callback
|
||||
*/
|
||||
function removeUser(db, username, options, callback) {
|
||||
const Db = require('../db');
|
||||
|
||||
// Attempt to execute command
|
||||
executeAuthRemoveUserCommand(db, username, options, (err, result) => {
|
||||
if (err && err.code === -5000) {
|
||||
const finalOptions = applyWriteConcern(Object.assign({}, options), { db }, options);
|
||||
// If we have another db set
|
||||
const db = options.dbName ? new Db(options.dbName, db.s.topology, db.s.options) : db;
|
||||
|
||||
// Fetch a user collection
|
||||
const collection = db.collection(CONSTANTS.SYSTEM_USER_COLLECTION);
|
||||
|
||||
// Locate the user
|
||||
findOne(collection, { user: username }, finalOptions, (err, user) => {
|
||||
if (user == null) return handleCallback(callback, err, false);
|
||||
remove(collection, { user: username }, finalOptions, err => {
|
||||
handleCallback(callback, err, true);
|
||||
});
|
||||
});
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (err) return handleCallback(callback, err);
|
||||
handleCallback(callback, err, result);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the current profiling level of MongoDB
|
||||
*
|
||||
* @method
|
||||
* @param {Db} db The Db instance on which to execute the command.
|
||||
* @param {string} level The new profiling level (off, slow_only, all).
|
||||
* @param {Object} [options] Optional settings. See Db.prototype.setProfilingLevel for a list of options.
|
||||
* @param {Db~resultCallback} [callback] The command result callback.
|
||||
*/
|
||||
function setProfilingLevel(db, level, options, callback) {
|
||||
const command = {};
|
||||
let profile = 0;
|
||||
|
||||
if (level === 'off') {
|
||||
profile = 0;
|
||||
} else if (level === 'slow_only') {
|
||||
profile = 1;
|
||||
} else if (level === 'all') {
|
||||
profile = 2;
|
||||
} else {
|
||||
return callback(new Error('Error: illegal profiling level value ' + level));
|
||||
}
|
||||
|
||||
// Set up the profile number
|
||||
command['profile'] = profile;
|
||||
|
||||
executeCommand(db, command, options, (err, doc) => {
|
||||
if (err == null && doc.ok === 1) return callback(null, level);
|
||||
return err != null
|
||||
? callback(err, null)
|
||||
: callback(new Error('Error with profile command'), null);
|
||||
});
|
||||
}
|
||||
|
||||
// Validate the database name
|
||||
function validateDatabaseName(databaseName) {
|
||||
if (typeof databaseName !== 'string')
|
||||
throw MongoError.create({ message: 'database name must be a string', driver: true });
|
||||
if (databaseName.length === 0)
|
||||
throw MongoError.create({ message: 'database name cannot be the empty string', driver: true });
|
||||
if (databaseName === '$external') return;
|
||||
|
||||
const invalidChars = [' ', '.', '$', '/', '\\'];
|
||||
for (let i = 0; i < invalidChars.length; i++) {
|
||||
if (databaseName.indexOf(invalidChars[i]) !== -1)
|
||||
throw MongoError.create({
|
||||
message: "database names cannot contain the character '" + invalidChars[i] + "'",
|
||||
driver: true
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Create the command object for Db.prototype.createIndex.
|
||||
*
|
||||
* @param {Db} db The Db instance on which to create the command.
|
||||
* @param {string} name Name of the collection to create the index on.
|
||||
* @param {(string|object)} fieldOrSpec Defines the index.
|
||||
* @param {Object} [options] Optional settings. See Db.prototype.createIndex for a list of options.
|
||||
* @return {Object} The insert command object.
|
||||
*/
|
||||
function createCreateIndexCommand(db, name, fieldOrSpec, options) {
|
||||
const indexParameters = parseIndexOptions(fieldOrSpec);
|
||||
const fieldHash = indexParameters.fieldHash;
|
||||
|
||||
// Generate the index name
|
||||
const indexName = typeof options.name === 'string' ? options.name : indexParameters.name;
|
||||
const selector = {
|
||||
ns: db.databaseName + '.' + name,
|
||||
key: fieldHash,
|
||||
name: indexName
|
||||
};
|
||||
|
||||
// Ensure we have a correct finalUnique
|
||||
const finalUnique = options == null || 'object' === typeof options ? false : options;
|
||||
// Set up options
|
||||
options = options == null || typeof options === 'boolean' ? {} : options;
|
||||
|
||||
// Add all the options
|
||||
const keysToOmit = Object.keys(selector);
|
||||
for (let optionName in options) {
|
||||
if (keysToOmit.indexOf(optionName) === -1) {
|
||||
selector[optionName] = options[optionName];
|
||||
}
|
||||
}
|
||||
|
||||
if (selector['unique'] == null) selector['unique'] = finalUnique;
|
||||
|
||||
// Remove any write concern operations
|
||||
const removeKeys = ['w', 'wtimeout', 'j', 'fsync', 'readPreference', 'session'];
|
||||
for (let i = 0; i < removeKeys.length; i++) {
|
||||
delete selector[removeKeys[i]];
|
||||
}
|
||||
|
||||
// Return the command creation selector
|
||||
return selector;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create index using the createIndexes command.
|
||||
*
|
||||
* @param {Db} db The Db instance on which to execute the command.
|
||||
* @param {string} name Name of the collection to create the index on.
|
||||
* @param {(string|object)} fieldOrSpec Defines the index.
|
||||
* @param {Object} [options] Optional settings. See Db.prototype.createIndex for a list of options.
|
||||
* @param {Db~resultCallback} [callback] The command result callback.
|
||||
*/
|
||||
function createIndexUsingCreateIndexes(db, name, fieldOrSpec, options, callback) {
|
||||
// Build the index
|
||||
const indexParameters = parseIndexOptions(fieldOrSpec);
|
||||
// Generate the index name
|
||||
const indexName = typeof options.name === 'string' ? options.name : indexParameters.name;
|
||||
// Set up the index
|
||||
const indexes = [{ name: indexName, key: indexParameters.fieldHash }];
|
||||
// merge all the options
|
||||
const keysToOmit = Object.keys(indexes[0]).concat([
|
||||
'writeConcern',
|
||||
'w',
|
||||
'wtimeout',
|
||||
'j',
|
||||
'fsync',
|
||||
'readPreference',
|
||||
'session'
|
||||
]);
|
||||
|
||||
for (let optionName in options) {
|
||||
if (keysToOmit.indexOf(optionName) === -1) {
|
||||
indexes[0][optionName] = options[optionName];
|
||||
}
|
||||
}
|
||||
|
||||
// Get capabilities
|
||||
const capabilities = db.s.topology.capabilities();
|
||||
|
||||
// Did the user pass in a collation, check if our write server supports it
|
||||
if (indexes[0].collation && capabilities && !capabilities.commandsTakeCollation) {
|
||||
// Create a new error
|
||||
const error = new MongoError('server/primary/mongos does not support collation');
|
||||
error.code = 67;
|
||||
// Return the error
|
||||
return callback(error);
|
||||
}
|
||||
|
||||
// Create command, apply write concern to command
|
||||
const cmd = applyWriteConcern({ createIndexes: name, indexes }, { db }, options);
|
||||
|
||||
// ReadPreference primary
|
||||
options.readPreference = ReadPreference.PRIMARY;
|
||||
|
||||
// Build the command
|
||||
executeCommand(db, cmd, options, (err, result) => {
|
||||
if (err) return handleCallback(callback, err, null);
|
||||
if (result.ok === 0) return handleCallback(callback, toError(result), null);
|
||||
// Return the indexName for backward compatibility
|
||||
handleCallback(callback, null, indexName);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Run the createUser command.
|
||||
*
|
||||
* @param {Db} db The Db instance on which to execute the command.
|
||||
* @param {string} username The username of the user to add.
|
||||
* @param {string} password The password of the user to add.
|
||||
* @param {object} [options] Optional settings. See Db.prototype.addUser for a list of options.
|
||||
* @param {Db~resultCallback} [callback] The command result callback
|
||||
*/
|
||||
function executeAuthCreateUserCommand(db, username, password, options, callback) {
|
||||
// Special case where there is no password ($external users)
|
||||
if (typeof username === 'string' && password != null && typeof password === 'object') {
|
||||
options = password;
|
||||
password = null;
|
||||
}
|
||||
|
||||
// Unpack all options
|
||||
if (typeof options === 'function') {
|
||||
callback = options;
|
||||
options = {};
|
||||
}
|
||||
|
||||
// Error out if we digestPassword set
|
||||
if (options.digestPassword != null) {
|
||||
return callback(
|
||||
toError(
|
||||
"The digestPassword option is not supported via add_user. Please use db.command('createUser', ...) instead for this option."
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
// Get additional values
|
||||
const customData = options.customData != null ? options.customData : {};
|
||||
let roles = Array.isArray(options.roles) ? options.roles : [];
|
||||
const maxTimeMS = typeof options.maxTimeMS === 'number' ? options.maxTimeMS : null;
|
||||
|
||||
// If not roles defined print deprecated message
|
||||
if (roles.length === 0) {
|
||||
console.log('Creating a user without roles is deprecated in MongoDB >= 2.6');
|
||||
}
|
||||
|
||||
// Get the error options
|
||||
const commandOptions = { writeCommand: true };
|
||||
if (options['dbName']) commandOptions.dbName = options['dbName'];
|
||||
|
||||
// Add maxTimeMS to options if set
|
||||
if (maxTimeMS != null) commandOptions.maxTimeMS = maxTimeMS;
|
||||
|
||||
// Check the db name and add roles if needed
|
||||
if (
|
||||
(db.databaseName.toLowerCase() === 'admin' || options.dbName === 'admin') &&
|
||||
!Array.isArray(options.roles)
|
||||
) {
|
||||
roles = ['root'];
|
||||
} else if (!Array.isArray(options.roles)) {
|
||||
roles = ['dbOwner'];
|
||||
}
|
||||
|
||||
const digestPassword = db.s.topology.lastIsMaster().maxWireVersion >= 7;
|
||||
|
||||
// Build the command to execute
|
||||
let command = {
|
||||
createUser: username,
|
||||
customData: customData,
|
||||
roles: roles,
|
||||
digestPassword
|
||||
};
|
||||
|
||||
// Apply write concern to command
|
||||
command = applyWriteConcern(command, { db }, options);
|
||||
|
||||
let userPassword = password;
|
||||
|
||||
if (!digestPassword) {
|
||||
// Use node md5 generator
|
||||
const md5 = crypto.createHash('md5');
|
||||
// Generate keys used for authentication
|
||||
md5.update(username + ':mongo:' + password);
|
||||
userPassword = md5.digest('hex');
|
||||
}
|
||||
|
||||
// No password
|
||||
if (typeof password === 'string') {
|
||||
command.pwd = userPassword;
|
||||
}
|
||||
|
||||
// Force write using primary
|
||||
commandOptions.readPreference = ReadPreference.primary;
|
||||
|
||||
// Execute the command
|
||||
executeCommand(db, command, commandOptions, (err, result) => {
|
||||
if (err && err.ok === 0 && err.code === undefined)
|
||||
return handleCallback(callback, { code: -5000 }, null);
|
||||
if (err) return handleCallback(callback, err, null);
|
||||
handleCallback(
|
||||
callback,
|
||||
!result.ok ? toError(result) : null,
|
||||
result.ok ? [{ user: username, pwd: '' }] : null
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Run the dropUser command.
|
||||
*
|
||||
* @param {Db} db The Db instance on which to execute the command.
|
||||
* @param {string} username The username of the user to remove.
|
||||
* @param {object} [options] Optional settings. See Db.prototype.removeUser for a list of options.
|
||||
* @param {Db~resultCallback} [callback] The command result callback
|
||||
*/
|
||||
function executeAuthRemoveUserCommand(db, username, options, callback) {
|
||||
if (typeof options === 'function') (callback = options), (options = {});
|
||||
options = options || {};
|
||||
|
||||
// Did the user destroy the topology
|
||||
if (db.serverConfig && db.serverConfig.isDestroyed())
|
||||
return callback(new MongoError('topology was destroyed'));
|
||||
// Get the error options
|
||||
const commandOptions = { writeCommand: true };
|
||||
if (options['dbName']) commandOptions.dbName = options['dbName'];
|
||||
|
||||
// Get additional values
|
||||
const maxTimeMS = typeof options.maxTimeMS === 'number' ? options.maxTimeMS : null;
|
||||
|
||||
// Add maxTimeMS to options if set
|
||||
if (maxTimeMS != null) commandOptions.maxTimeMS = maxTimeMS;
|
||||
|
||||
// Build the command to execute
|
||||
let command = {
|
||||
dropUser: username
|
||||
};
|
||||
|
||||
// Apply write concern to command
|
||||
command = applyWriteConcern(command, { db }, options);
|
||||
|
||||
// Force write using primary
|
||||
commandOptions.readPreference = ReadPreference.primary;
|
||||
|
||||
// Execute the command
|
||||
executeCommand(db, command, commandOptions, (err, result) => {
|
||||
if (err && !err.ok && err.code === undefined) return handleCallback(callback, { code: -5000 });
|
||||
if (err) return handleCallback(callback, err, null);
|
||||
handleCallback(callback, null, result.ok ? true : false);
|
||||
});
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
addUser,
|
||||
collections,
|
||||
createCollection,
|
||||
createListener,
|
||||
createIndex,
|
||||
dropCollection,
|
||||
dropDatabase,
|
||||
ensureIndex,
|
||||
evaluate,
|
||||
executeCommand,
|
||||
executeDbAdminCommand,
|
||||
listCollectionsTransforms,
|
||||
indexInformation,
|
||||
profilingInfo,
|
||||
profilingLevel,
|
||||
removeUser,
|
||||
setProfilingLevel,
|
||||
validateDatabaseName
|
||||
};
|
||||
+646
@@ -0,0 +1,646 @@
|
||||
'use strict';
|
||||
|
||||
const authenticate = require('../authenticate');
|
||||
const deprecate = require('util').deprecate;
|
||||
const Logger = require('mongodb-core').Logger;
|
||||
const MongoError = require('mongodb-core').MongoError;
|
||||
const Mongos = require('../topologies/mongos');
|
||||
const parse = require('mongodb-core').parseConnectionString;
|
||||
const ReadPreference = require('mongodb-core').ReadPreference;
|
||||
const ReplSet = require('../topologies/replset');
|
||||
const Server = require('../topologies/server');
|
||||
const ServerSessionPool = require('mongodb-core').Sessions.ServerSessionPool;
|
||||
|
||||
const monitoringEvents = [
|
||||
'timeout',
|
||||
'close',
|
||||
'serverOpening',
|
||||
'serverDescriptionChanged',
|
||||
'serverHeartbeatStarted',
|
||||
'serverHeartbeatSucceeded',
|
||||
'serverHeartbeatFailed',
|
||||
'serverClosed',
|
||||
'topologyOpening',
|
||||
'topologyClosed',
|
||||
'topologyDescriptionChanged',
|
||||
'commandStarted',
|
||||
'commandSucceeded',
|
||||
'commandFailed',
|
||||
'joined',
|
||||
'left',
|
||||
'ping',
|
||||
'ha',
|
||||
'all',
|
||||
'fullsetup',
|
||||
'open'
|
||||
];
|
||||
const ignoreOptionNames = ['native_parser'];
|
||||
const legacyOptionNames = ['server', 'replset', 'replSet', 'mongos', 'db'];
|
||||
const legacyParse = deprecate(
|
||||
require('../url_parser'),
|
||||
'current URL string parser is deprecated, and will be removed in a future version. ' +
|
||||
'To use the new parser, pass option { useNewUrlParser: true } to MongoClient.connect.'
|
||||
);
|
||||
const validOptionNames = [
|
||||
'poolSize',
|
||||
'ssl',
|
||||
'sslValidate',
|
||||
'sslCA',
|
||||
'sslCert',
|
||||
'sslKey',
|
||||
'sslPass',
|
||||
'sslCRL',
|
||||
'autoReconnect',
|
||||
'noDelay',
|
||||
'keepAlive',
|
||||
'keepAliveInitialDelay',
|
||||
'connectTimeoutMS',
|
||||
'family',
|
||||
'socketTimeoutMS',
|
||||
'reconnectTries',
|
||||
'reconnectInterval',
|
||||
'ha',
|
||||
'haInterval',
|
||||
'replicaSet',
|
||||
'secondaryAcceptableLatencyMS',
|
||||
'acceptableLatencyMS',
|
||||
'connectWithNoPrimary',
|
||||
'authSource',
|
||||
'w',
|
||||
'wtimeout',
|
||||
'j',
|
||||
'forceServerObjectId',
|
||||
'serializeFunctions',
|
||||
'ignoreUndefined',
|
||||
'raw',
|
||||
'bufferMaxEntries',
|
||||
'readPreference',
|
||||
'pkFactory',
|
||||
'promiseLibrary',
|
||||
'readConcern',
|
||||
'maxStalenessSeconds',
|
||||
'loggerLevel',
|
||||
'logger',
|
||||
'promoteValues',
|
||||
'promoteBuffers',
|
||||
'promoteLongs',
|
||||
'domainsEnabled',
|
||||
'checkServerIdentity',
|
||||
'validateOptions',
|
||||
'appname',
|
||||
'auth',
|
||||
'user',
|
||||
'password',
|
||||
'authMechanism',
|
||||
'compression',
|
||||
'fsync',
|
||||
'readPreferenceTags',
|
||||
'numberOfRetries',
|
||||
'auto_reconnect',
|
||||
'minSize',
|
||||
'monitorCommands',
|
||||
'retryWrites',
|
||||
'useNewUrlParser'
|
||||
];
|
||||
|
||||
function addListeners(mongoClient, topology) {
|
||||
topology.on('authenticated', createListener(mongoClient, 'authenticated'));
|
||||
topology.on('error', createListener(mongoClient, 'error'));
|
||||
topology.on('timeout', createListener(mongoClient, 'timeout'));
|
||||
topology.on('close', createListener(mongoClient, 'close'));
|
||||
topology.on('parseError', createListener(mongoClient, 'parseError'));
|
||||
topology.once('open', createListener(mongoClient, 'open'));
|
||||
topology.once('fullsetup', createListener(mongoClient, 'fullsetup'));
|
||||
topology.once('all', createListener(mongoClient, 'all'));
|
||||
topology.on('reconnect', createListener(mongoClient, 'reconnect'));
|
||||
}
|
||||
|
||||
function assignTopology(client, topology) {
|
||||
client.topology = topology;
|
||||
topology.s.sessionPool = new ServerSessionPool(topology.s.coreTopology);
|
||||
}
|
||||
|
||||
// Clear out all events
|
||||
function clearAllEvents(topology) {
|
||||
monitoringEvents.forEach(event => topology.removeAllListeners(event));
|
||||
}
|
||||
|
||||
// Collect all events in order from SDAM
|
||||
function collectEvents(mongoClient, topology) {
|
||||
const MongoClient = require('../mongo_client');
|
||||
const collectedEvents = [];
|
||||
|
||||
if (mongoClient instanceof MongoClient) {
|
||||
monitoringEvents.forEach(event => {
|
||||
topology.on(event, (object1, object2) => {
|
||||
if (event === 'open') {
|
||||
collectedEvents.push({ event: event, object1: mongoClient });
|
||||
} else {
|
||||
collectedEvents.push({ event: event, object1: object1, object2: object2 });
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
return collectedEvents;
|
||||
}
|
||||
|
||||
/**
|
||||
* Connect to MongoDB using a url as documented at
|
||||
*
|
||||
* docs.mongodb.org/manual/reference/connection-string/
|
||||
*
|
||||
* Note that for replicasets the replicaSet query parameter is required in the 2.0 driver
|
||||
*
|
||||
* @method
|
||||
* @param {MongoClient} mongoClient The MongoClient instance with which to connect.
|
||||
* @param {string} url The connection URI string
|
||||
* @param {object} [options] Optional settings. See MongoClient.prototype.connect for a list of options.
|
||||
* @param {MongoClient~connectCallback} [callback] The command result callback
|
||||
*/
|
||||
function connect(mongoClient, url, options, callback) {
|
||||
options = Object.assign({}, options);
|
||||
|
||||
// If callback is null throw an exception
|
||||
if (callback == null) {
|
||||
throw new Error('no callback function provided');
|
||||
}
|
||||
|
||||
// Get a logger for MongoClient
|
||||
const logger = Logger('MongoClient', options);
|
||||
|
||||
// Did we pass in a Server/ReplSet/Mongos
|
||||
if (url instanceof Server || url instanceof ReplSet || url instanceof Mongos) {
|
||||
return connectWithUrl(mongoClient, url, options, connectCallback);
|
||||
}
|
||||
|
||||
const parseFn = options.useNewUrlParser ? parse : legacyParse;
|
||||
const transform = options.useNewUrlParser ? transformUrlOptions : legacyTransformUrlOptions;
|
||||
|
||||
parseFn(url, options, (err, _object) => {
|
||||
// Do not attempt to connect if parsing error
|
||||
if (err) return callback(err);
|
||||
|
||||
// Flatten
|
||||
const object = transform(_object);
|
||||
|
||||
// Parse the string
|
||||
const _finalOptions = createUnifiedOptions(object, options);
|
||||
|
||||
// Check if we have connection and socket timeout set
|
||||
if (_finalOptions.socketTimeoutMS == null) _finalOptions.socketTimeoutMS = 360000;
|
||||
if (_finalOptions.connectTimeoutMS == null) _finalOptions.connectTimeoutMS = 30000;
|
||||
|
||||
if (_finalOptions.db_options && _finalOptions.db_options.auth) {
|
||||
delete _finalOptions.db_options.auth;
|
||||
}
|
||||
|
||||
// Store the merged options object
|
||||
mongoClient.s.options = _finalOptions;
|
||||
|
||||
// Failure modes
|
||||
if (object.servers.length === 0) {
|
||||
return callback(new Error('connection string must contain at least one seed host'));
|
||||
}
|
||||
|
||||
// Do we have a replicaset then skip discovery and go straight to connectivity
|
||||
if (_finalOptions.replicaSet || _finalOptions.rs_name) {
|
||||
return createTopology(
|
||||
mongoClient,
|
||||
'replicaset',
|
||||
_finalOptions,
|
||||
connectHandler(mongoClient, _finalOptions, connectCallback)
|
||||
);
|
||||
} else if (object.servers.length > 1) {
|
||||
return createTopology(
|
||||
mongoClient,
|
||||
'mongos',
|
||||
_finalOptions,
|
||||
connectHandler(mongoClient, _finalOptions, connectCallback)
|
||||
);
|
||||
} else {
|
||||
return createServer(
|
||||
mongoClient,
|
||||
_finalOptions,
|
||||
connectHandler(mongoClient, _finalOptions, connectCallback)
|
||||
);
|
||||
}
|
||||
});
|
||||
function connectCallback(err, topology) {
|
||||
const warningMessage = `seed list contains no mongos proxies, replicaset connections requires the parameter replicaSet to be supplied in the URI or options object, mongodb://server:port/db?replicaSet=name`;
|
||||
if (err && err.message === 'no mongos proxies found in seed list') {
|
||||
if (logger.isWarn()) {
|
||||
logger.warn(warningMessage);
|
||||
}
|
||||
|
||||
// Return a more specific error message for MongoClient.connect
|
||||
return callback(new MongoError(warningMessage));
|
||||
}
|
||||
|
||||
// Return the error and db instance
|
||||
callback(err, topology);
|
||||
}
|
||||
}
|
||||
|
||||
function connectHandler(client, options, callback) {
|
||||
return (err, topology) => {
|
||||
if (err) {
|
||||
return handleConnectCallback(err, topology, callback);
|
||||
}
|
||||
|
||||
// No authentication just reconnect
|
||||
if (!options.auth) {
|
||||
return handleConnectCallback(err, topology, callback);
|
||||
}
|
||||
|
||||
// Authenticate
|
||||
authenticate(client, options.user, options.password, options, (err, success) => {
|
||||
if (success) {
|
||||
handleConnectCallback(null, topology, callback);
|
||||
} else {
|
||||
if (topology) topology.close();
|
||||
const authError = err ? err : new Error('Could not authenticate user ' + options.auth[0]);
|
||||
handleConnectCallback(authError, topology, callback);
|
||||
}
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Connect to MongoDB using a url as documented at
|
||||
*
|
||||
* docs.mongodb.org/manual/reference/connection-string/
|
||||
*
|
||||
* Note that for replicasets the replicaSet query parameter is required in the 2.0 driver
|
||||
*
|
||||
* @method
|
||||
* @param {MongoClient} mongoClient The MongoClient instance with which to connect.
|
||||
* @param {MongoClient~connectCallback} [callback] The command result callback
|
||||
*/
|
||||
function connectOp(mongoClient, err, callback) {
|
||||
// Did we have a validation error
|
||||
if (err) return callback(err);
|
||||
// Fallback to callback based connect
|
||||
connect(mongoClient, mongoClient.s.url, mongoClient.s.options, err => {
|
||||
if (err) return callback(err);
|
||||
callback(null, mongoClient);
|
||||
});
|
||||
}
|
||||
|
||||
function connectWithUrl(mongoClient, url, options, connectCallback) {
|
||||
// Set the topology
|
||||
assignTopology(mongoClient, url);
|
||||
|
||||
// Add listeners
|
||||
addListeners(mongoClient, url);
|
||||
|
||||
// Propagate the events to the client
|
||||
relayEvents(mongoClient, url);
|
||||
|
||||
let finalOptions = Object.assign({}, options);
|
||||
|
||||
// If we have a readPreference passed in by the db options, convert it from a string
|
||||
if (typeof options.readPreference === 'string' || typeof options.read_preference === 'string') {
|
||||
finalOptions.readPreference = new ReadPreference(
|
||||
options.readPreference || options.read_preference
|
||||
);
|
||||
}
|
||||
|
||||
// Connect
|
||||
return url.connect(
|
||||
finalOptions,
|
||||
connectHandler(mongoClient, finalOptions, (err, topology) => {
|
||||
if (err) return connectCallback(err, topology);
|
||||
if (finalOptions.user || finalOptions.password || finalOptions.authMechanism) {
|
||||
return authenticate(
|
||||
mongoClient,
|
||||
finalOptions.user,
|
||||
finalOptions.password,
|
||||
finalOptions,
|
||||
err => {
|
||||
if (err) return connectCallback(err, topology);
|
||||
connectCallback(err, topology);
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
connectCallback(err, topology);
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
function createListener(mongoClient, event) {
|
||||
const eventSet = new Set(['all', 'fullsetup', 'open', 'reconnect']);
|
||||
return (v1, v2) => {
|
||||
if (eventSet.has(event)) {
|
||||
return mongoClient.emit(event, mongoClient);
|
||||
}
|
||||
|
||||
mongoClient.emit(event, v1, v2);
|
||||
};
|
||||
}
|
||||
|
||||
function createServer(mongoClient, options, callback) {
|
||||
// Pass in the promise library
|
||||
options.promiseLibrary = mongoClient.s.promiseLibrary;
|
||||
|
||||
// Set default options
|
||||
const servers = translateOptions(options);
|
||||
|
||||
const server = servers[0];
|
||||
|
||||
// Propagate the events to the client
|
||||
const collectedEvents = collectEvents(mongoClient, server);
|
||||
|
||||
// Connect to topology
|
||||
server.connect(options, (err, topology) => {
|
||||
if (err) {
|
||||
server.close(true);
|
||||
return callback(err);
|
||||
}
|
||||
// Clear out all the collected event listeners
|
||||
clearAllEvents(server);
|
||||
|
||||
// Relay all the events
|
||||
relayEvents(mongoClient, server);
|
||||
// Add listeners
|
||||
addListeners(mongoClient, server);
|
||||
// Check if we are really speaking to a mongos
|
||||
const ismaster = topology.lastIsMaster();
|
||||
|
||||
// Set the topology
|
||||
assignTopology(mongoClient, topology);
|
||||
|
||||
// Do we actually have a mongos
|
||||
if (ismaster && ismaster.msg === 'isdbgrid') {
|
||||
// Destroy the current connection
|
||||
topology.close();
|
||||
// Create mongos connection instead
|
||||
return createTopology(mongoClient, 'mongos', options, callback);
|
||||
}
|
||||
|
||||
// Fire all the events
|
||||
replayEvents(mongoClient, collectedEvents);
|
||||
// Otherwise callback
|
||||
callback(err, topology);
|
||||
});
|
||||
}
|
||||
|
||||
function createTopology(mongoClient, topologyType, options, callback) {
|
||||
// Pass in the promise library
|
||||
options.promiseLibrary = mongoClient.s.promiseLibrary;
|
||||
|
||||
const translationOptions = {};
|
||||
if (topologyType === 'unified') translationOptions.createServers = false;
|
||||
|
||||
// Set default options
|
||||
const servers = translateOptions(options, translationOptions);
|
||||
|
||||
// Create the topology
|
||||
let topology;
|
||||
if (topologyType === 'mongos') {
|
||||
topology = new Mongos(servers, options);
|
||||
} else if (topologyType === 'replicaset') {
|
||||
topology = new ReplSet(servers, options);
|
||||
}
|
||||
|
||||
// Add listeners
|
||||
addListeners(mongoClient, topology);
|
||||
|
||||
// Propagate the events to the client
|
||||
relayEvents(mongoClient, topology);
|
||||
|
||||
// Open the connection
|
||||
topology.connect(options, (err, newTopology) => {
|
||||
if (err) {
|
||||
topology.close(true);
|
||||
return callback(err);
|
||||
}
|
||||
|
||||
assignTopology(mongoClient, newTopology);
|
||||
callback(null, newTopology);
|
||||
});
|
||||
}
|
||||
|
||||
function createUnifiedOptions(finalOptions, options) {
|
||||
const childOptions = [
|
||||
'mongos',
|
||||
'server',
|
||||
'db',
|
||||
'replset',
|
||||
'db_options',
|
||||
'server_options',
|
||||
'rs_options',
|
||||
'mongos_options'
|
||||
];
|
||||
const noMerge = ['readconcern', 'compression'];
|
||||
|
||||
for (const name in options) {
|
||||
if (noMerge.indexOf(name.toLowerCase()) !== -1) {
|
||||
finalOptions[name] = options[name];
|
||||
} else if (childOptions.indexOf(name.toLowerCase()) !== -1) {
|
||||
finalOptions = mergeOptions(finalOptions, options[name], false);
|
||||
} else {
|
||||
if (
|
||||
options[name] &&
|
||||
typeof options[name] === 'object' &&
|
||||
!Buffer.isBuffer(options[name]) &&
|
||||
!Array.isArray(options[name])
|
||||
) {
|
||||
finalOptions = mergeOptions(finalOptions, options[name], true);
|
||||
} else {
|
||||
finalOptions[name] = options[name];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return finalOptions;
|
||||
}
|
||||
|
||||
function handleConnectCallback(err, topology, callback) {
|
||||
return process.nextTick(() => {
|
||||
try {
|
||||
callback(err, topology);
|
||||
} catch (err) {
|
||||
if (topology) topology.close();
|
||||
throw err;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function legacyTransformUrlOptions(object) {
|
||||
return mergeOptions(createUnifiedOptions({}, object), object, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Logout user from server, fire off on all connections and remove all auth info.
|
||||
*
|
||||
* @method
|
||||
* @param {MongoClient} mongoClient The MongoClient instance on which to logout.
|
||||
* @param {object} [options] Optional settings. See MongoClient.prototype.logout for a list of options.
|
||||
* @param {Db~resultCallback} [callback] The command result callback
|
||||
*/
|
||||
function logout(mongoClient, dbName, callback) {
|
||||
mongoClient.topology.logout(dbName, err => {
|
||||
if (err) return callback(err);
|
||||
callback(null, true);
|
||||
});
|
||||
}
|
||||
|
||||
function mergeOptions(target, source, flatten) {
|
||||
for (const name in source) {
|
||||
if (source[name] && typeof source[name] === 'object' && flatten) {
|
||||
target = mergeOptions(target, source[name], flatten);
|
||||
} else {
|
||||
target[name] = source[name];
|
||||
}
|
||||
}
|
||||
|
||||
return target;
|
||||
}
|
||||
|
||||
function relayEvents(mongoClient, topology) {
|
||||
const serverOrCommandEvents = [
|
||||
'serverOpening',
|
||||
'serverDescriptionChanged',
|
||||
'serverHeartbeatStarted',
|
||||
'serverHeartbeatSucceeded',
|
||||
'serverHeartbeatFailed',
|
||||
'serverClosed',
|
||||
'topologyOpening',
|
||||
'topologyClosed',
|
||||
'topologyDescriptionChanged',
|
||||
'commandStarted',
|
||||
'commandSucceeded',
|
||||
'commandFailed',
|
||||
'joined',
|
||||
'left',
|
||||
'ping',
|
||||
'ha'
|
||||
];
|
||||
|
||||
serverOrCommandEvents.forEach(event => {
|
||||
topology.on(event, (object1, object2) => {
|
||||
mongoClient.emit(event, object1, object2);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
//
|
||||
// Replay any events due to single server connection switching to Mongos
|
||||
//
|
||||
function replayEvents(mongoClient, events) {
|
||||
for (let i = 0; i < events.length; i++) {
|
||||
mongoClient.emit(events[i].event, events[i].object1, events[i].object2);
|
||||
}
|
||||
}
|
||||
|
||||
const LEGACY_OPTIONS_MAP = validOptionNames.reduce((obj, name) => {
|
||||
obj[name.toLowerCase()] = name;
|
||||
return obj;
|
||||
}, {});
|
||||
|
||||
function transformUrlOptions(_object) {
|
||||
let object = Object.assign({ servers: _object.hosts }, _object.options);
|
||||
for (let name in object) {
|
||||
const camelCaseName = LEGACY_OPTIONS_MAP[name];
|
||||
if (camelCaseName) {
|
||||
object[camelCaseName] = object[name];
|
||||
}
|
||||
}
|
||||
if (_object.auth) {
|
||||
const auth = _object.auth;
|
||||
for (let i in auth) {
|
||||
if (auth[i]) {
|
||||
object[i] = auth[i];
|
||||
}
|
||||
}
|
||||
|
||||
if (auth.username) {
|
||||
object.auth = auth;
|
||||
object.user = auth.username;
|
||||
}
|
||||
|
||||
if (auth.db) {
|
||||
object.authSource = object.authSource || auth.db;
|
||||
}
|
||||
}
|
||||
|
||||
if (_object.defaultDatabase) {
|
||||
object.dbName = _object.defaultDatabase;
|
||||
}
|
||||
|
||||
if (object.maxpoolsize) {
|
||||
object.poolSize = object.maxpoolsize;
|
||||
}
|
||||
|
||||
if (object.readconcernlevel) {
|
||||
object.readConcern = { level: object.readconcernlevel };
|
||||
}
|
||||
|
||||
if (object.wtimeoutms) {
|
||||
object.wtimeout = object.wtimeoutms;
|
||||
}
|
||||
|
||||
return object;
|
||||
}
|
||||
|
||||
function translateOptions(options, translationOptions) {
|
||||
translationOptions = Object.assign({}, { createServers: true }, translationOptions);
|
||||
|
||||
// If we have a readPreference passed in by the db options
|
||||
if (typeof options.readPreference === 'string' || typeof options.read_preference === 'string') {
|
||||
options.readPreference = new ReadPreference(options.readPreference || options.read_preference);
|
||||
}
|
||||
|
||||
// Do we have readPreference tags, add them
|
||||
if (options.readPreference && (options.readPreferenceTags || options.read_preference_tags)) {
|
||||
options.readPreference.tags = options.readPreferenceTags || options.read_preference_tags;
|
||||
}
|
||||
|
||||
// Do we have maxStalenessSeconds
|
||||
if (options.maxStalenessSeconds) {
|
||||
options.readPreference.maxStalenessSeconds = options.maxStalenessSeconds;
|
||||
}
|
||||
|
||||
// Set the socket and connection timeouts
|
||||
if (options.socketTimeoutMS == null) options.socketTimeoutMS = 360000;
|
||||
if (options.connectTimeoutMS == null) options.connectTimeoutMS = 30000;
|
||||
|
||||
if (!translationOptions.createServers) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Create server instances
|
||||
return options.servers.map(serverObj => {
|
||||
return serverObj.domain_socket
|
||||
? new Server(serverObj.domain_socket, 27017, options)
|
||||
: new Server(serverObj.host, serverObj.port, options);
|
||||
});
|
||||
}
|
||||
|
||||
// Validate options object
|
||||
function validOptions(options) {
|
||||
const _validOptions = validOptionNames.concat(legacyOptionNames);
|
||||
|
||||
for (const name in options) {
|
||||
if (ignoreOptionNames.indexOf(name) !== -1) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (_validOptions.indexOf(name) === -1 && options.validateOptions) {
|
||||
return new MongoError(`option ${name} is not supported`);
|
||||
} else if (_validOptions.indexOf(name) === -1) {
|
||||
console.warn(`the options [${name}] is not supported`);
|
||||
}
|
||||
|
||||
if (legacyOptionNames.indexOf(name) !== -1) {
|
||||
console.warn(
|
||||
`the server/replset/mongos/db options are deprecated, ` +
|
||||
`all their options are supported at the top level of the options object [${validOptionNames}]`
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = { connectOp, logout, validOptions };
|
||||
Reference in New Issue
Block a user