mongodb 3.3.0-beta1 → 3.3.0-beta2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/aggregation_cursor.js +3 -1
- package/lib/change_stream.js +183 -71
- package/lib/collection.js +8 -9
- package/lib/command_cursor.js +6 -4
- package/lib/core/connection/connect.js +1 -1
- package/lib/core/connection/pool.js +66 -41
- package/lib/core/cursor.js +70 -60
- package/lib/core/error.js +24 -1
- package/lib/core/sdam/server.js +84 -9
- package/lib/core/sdam/server_description.js +14 -0
- package/lib/core/sdam/topology.js +17 -4
- package/lib/core/sdam/topology_description.js +6 -14
- package/lib/core/topologies/mongos.js +1 -1
- package/lib/core/topologies/replset.js +1 -1
- package/lib/core/topologies/server.js +36 -1
- package/lib/core/utils.js +38 -1
- package/lib/core/wireprotocol/command.js +2 -2
- package/lib/cursor.js +7 -5
- package/lib/db.js +7 -1
- package/lib/error.js +10 -8
- package/lib/mongo_client.js +9 -13
- package/lib/operations/aggregate.js +17 -9
- package/lib/operations/close.js +47 -0
- package/lib/operations/collection_ops.js +4 -0
- package/lib/operations/command.js +2 -1
- package/lib/operations/command_v2.js +43 -0
- package/lib/operations/connect.js +83 -9
- package/lib/operations/create_collection.js +1 -0
- package/lib/operations/distinct.js +20 -21
- package/lib/operations/drop.js +1 -0
- package/lib/operations/execute_operation.js +81 -2
- package/lib/operations/explain.js +0 -4
- package/lib/operations/operation.js +3 -1
- package/package.json +1 -1
- package/lib/operations/mongo_client_ops.js +0 -731
|
@@ -1,731 +0,0 @@
|
|
|
1
|
-
'use strict';
|
|
2
|
-
|
|
3
|
-
const deprecate = require('util').deprecate;
|
|
4
|
-
const Logger = require('../core').Logger;
|
|
5
|
-
const MongoError = require('../core').MongoError;
|
|
6
|
-
const Mongos = require('../topologies/mongos');
|
|
7
|
-
const parse = require('../core').parseConnectionString;
|
|
8
|
-
const ReadPreference = require('../core').ReadPreference;
|
|
9
|
-
const ReplSet = require('../topologies/replset');
|
|
10
|
-
const Server = require('../topologies/server');
|
|
11
|
-
const ServerSessionPool = require('../core').Sessions.ServerSessionPool;
|
|
12
|
-
const NativeTopology = require('../topologies/native_topology');
|
|
13
|
-
const MongoCredentials = require('../core').MongoCredentials;
|
|
14
|
-
const ReadConcern = require('../read_concern');
|
|
15
|
-
const os = require('os');
|
|
16
|
-
|
|
17
|
-
let client;
|
|
18
|
-
function loadClient() {
|
|
19
|
-
if (!client) {
|
|
20
|
-
client = require('../mongo_client');
|
|
21
|
-
}
|
|
22
|
-
return client;
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
const monitoringEvents = [
|
|
26
|
-
'timeout',
|
|
27
|
-
'close',
|
|
28
|
-
'serverOpening',
|
|
29
|
-
'serverDescriptionChanged',
|
|
30
|
-
'serverHeartbeatStarted',
|
|
31
|
-
'serverHeartbeatSucceeded',
|
|
32
|
-
'serverHeartbeatFailed',
|
|
33
|
-
'serverClosed',
|
|
34
|
-
'topologyOpening',
|
|
35
|
-
'topologyClosed',
|
|
36
|
-
'topologyDescriptionChanged',
|
|
37
|
-
'commandStarted',
|
|
38
|
-
'commandSucceeded',
|
|
39
|
-
'commandFailed',
|
|
40
|
-
'joined',
|
|
41
|
-
'left',
|
|
42
|
-
'ping',
|
|
43
|
-
'ha',
|
|
44
|
-
'all',
|
|
45
|
-
'fullsetup',
|
|
46
|
-
'open'
|
|
47
|
-
];
|
|
48
|
-
const ignoreOptionNames = ['native_parser'];
|
|
49
|
-
const legacyOptionNames = ['server', 'replset', 'replSet', 'mongos', 'db'];
|
|
50
|
-
const legacyParse = deprecate(
|
|
51
|
-
require('../url_parser'),
|
|
52
|
-
'current URL string parser is deprecated, and will be removed in a future version. ' +
|
|
53
|
-
'To use the new parser, pass option { useNewUrlParser: true } to MongoClient.connect.'
|
|
54
|
-
);
|
|
55
|
-
const validOptionNames = [
|
|
56
|
-
'poolSize',
|
|
57
|
-
'ssl',
|
|
58
|
-
'sslValidate',
|
|
59
|
-
'sslCA',
|
|
60
|
-
'sslCert',
|
|
61
|
-
'sslKey',
|
|
62
|
-
'sslPass',
|
|
63
|
-
'sslCRL',
|
|
64
|
-
'autoReconnect',
|
|
65
|
-
'noDelay',
|
|
66
|
-
'keepAlive',
|
|
67
|
-
'keepAliveInitialDelay',
|
|
68
|
-
'connectTimeoutMS',
|
|
69
|
-
'family',
|
|
70
|
-
'socketTimeoutMS',
|
|
71
|
-
'reconnectTries',
|
|
72
|
-
'reconnectInterval',
|
|
73
|
-
'ha',
|
|
74
|
-
'haInterval',
|
|
75
|
-
'replicaSet',
|
|
76
|
-
'secondaryAcceptableLatencyMS',
|
|
77
|
-
'acceptableLatencyMS',
|
|
78
|
-
'connectWithNoPrimary',
|
|
79
|
-
'authSource',
|
|
80
|
-
'w',
|
|
81
|
-
'wtimeout',
|
|
82
|
-
'j',
|
|
83
|
-
'forceServerObjectId',
|
|
84
|
-
'serializeFunctions',
|
|
85
|
-
'ignoreUndefined',
|
|
86
|
-
'raw',
|
|
87
|
-
'bufferMaxEntries',
|
|
88
|
-
'readPreference',
|
|
89
|
-
'pkFactory',
|
|
90
|
-
'promiseLibrary',
|
|
91
|
-
'readConcern',
|
|
92
|
-
'maxStalenessSeconds',
|
|
93
|
-
'loggerLevel',
|
|
94
|
-
'logger',
|
|
95
|
-
'promoteValues',
|
|
96
|
-
'promoteBuffers',
|
|
97
|
-
'promoteLongs',
|
|
98
|
-
'domainsEnabled',
|
|
99
|
-
'checkServerIdentity',
|
|
100
|
-
'validateOptions',
|
|
101
|
-
'appname',
|
|
102
|
-
'auth',
|
|
103
|
-
'user',
|
|
104
|
-
'password',
|
|
105
|
-
'authMechanism',
|
|
106
|
-
'compression',
|
|
107
|
-
'fsync',
|
|
108
|
-
'readPreferenceTags',
|
|
109
|
-
'numberOfRetries',
|
|
110
|
-
'auto_reconnect',
|
|
111
|
-
'minSize',
|
|
112
|
-
'monitorCommands',
|
|
113
|
-
'retryWrites',
|
|
114
|
-
'useNewUrlParser',
|
|
115
|
-
'useUnifiedTopology',
|
|
116
|
-
'serverSelectionTimeoutMS',
|
|
117
|
-
'useRecoveryToken',
|
|
118
|
-
'autoEncryption'
|
|
119
|
-
];
|
|
120
|
-
|
|
121
|
-
function addListeners(mongoClient, topology) {
|
|
122
|
-
topology.on('authenticated', createListener(mongoClient, 'authenticated'));
|
|
123
|
-
topology.on('error', createListener(mongoClient, 'error'));
|
|
124
|
-
topology.on('timeout', createListener(mongoClient, 'timeout'));
|
|
125
|
-
topology.on('close', createListener(mongoClient, 'close'));
|
|
126
|
-
topology.on('parseError', createListener(mongoClient, 'parseError'));
|
|
127
|
-
topology.once('open', createListener(mongoClient, 'open'));
|
|
128
|
-
topology.once('fullsetup', createListener(mongoClient, 'fullsetup'));
|
|
129
|
-
topology.once('all', createListener(mongoClient, 'all'));
|
|
130
|
-
topology.on('reconnect', createListener(mongoClient, 'reconnect'));
|
|
131
|
-
}
|
|
132
|
-
|
|
133
|
-
function assignTopology(client, topology) {
|
|
134
|
-
client.topology = topology;
|
|
135
|
-
topology.s.sessionPool =
|
|
136
|
-
topology instanceof NativeTopology
|
|
137
|
-
? new ServerSessionPool(topology)
|
|
138
|
-
: new ServerSessionPool(topology.s.coreTopology);
|
|
139
|
-
}
|
|
140
|
-
|
|
141
|
-
// Clear out all events
|
|
142
|
-
function clearAllEvents(topology) {
|
|
143
|
-
monitoringEvents.forEach(event => topology.removeAllListeners(event));
|
|
144
|
-
}
|
|
145
|
-
|
|
146
|
-
// Collect all events in order from SDAM
|
|
147
|
-
function collectEvents(mongoClient, topology) {
|
|
148
|
-
let MongoClient = loadClient();
|
|
149
|
-
const collectedEvents = [];
|
|
150
|
-
|
|
151
|
-
if (mongoClient instanceof MongoClient) {
|
|
152
|
-
monitoringEvents.forEach(event => {
|
|
153
|
-
topology.on(event, (object1, object2) => {
|
|
154
|
-
if (event === 'open') {
|
|
155
|
-
collectedEvents.push({ event: event, object1: mongoClient });
|
|
156
|
-
} else {
|
|
157
|
-
collectedEvents.push({ event: event, object1: object1, object2: object2 });
|
|
158
|
-
}
|
|
159
|
-
});
|
|
160
|
-
});
|
|
161
|
-
}
|
|
162
|
-
|
|
163
|
-
return collectedEvents;
|
|
164
|
-
}
|
|
165
|
-
|
|
166
|
-
/**
|
|
167
|
-
* Connect to MongoDB using a url as documented at
|
|
168
|
-
*
|
|
169
|
-
* docs.mongodb.org/manual/reference/connection-string/
|
|
170
|
-
*
|
|
171
|
-
* Note that for replicasets the replicaSet query parameter is required in the 2.0 driver
|
|
172
|
-
*
|
|
173
|
-
* @method
|
|
174
|
-
* @param {MongoClient} mongoClient The MongoClient instance with which to connect.
|
|
175
|
-
* @param {string} url The connection URI string
|
|
176
|
-
* @param {object} [options] Optional settings. See MongoClient.prototype.connect for a list of options.
|
|
177
|
-
* @param {MongoClient~connectCallback} [callback] The command result callback
|
|
178
|
-
*/
|
|
179
|
-
function connect(mongoClient, url, options, callback) {
|
|
180
|
-
options = Object.assign({}, options);
|
|
181
|
-
|
|
182
|
-
// If callback is null throw an exception
|
|
183
|
-
if (callback == null) {
|
|
184
|
-
throw new Error('no callback function provided');
|
|
185
|
-
}
|
|
186
|
-
|
|
187
|
-
let didRequestAuthentication = false;
|
|
188
|
-
const logger = Logger('MongoClient', options);
|
|
189
|
-
|
|
190
|
-
// Did we pass in a Server/ReplSet/Mongos
|
|
191
|
-
if (url instanceof Server || url instanceof ReplSet || url instanceof Mongos) {
|
|
192
|
-
return connectWithUrl(mongoClient, url, options, connectCallback);
|
|
193
|
-
}
|
|
194
|
-
|
|
195
|
-
const parseFn = options.useNewUrlParser ? parse : legacyParse;
|
|
196
|
-
const transform = options.useNewUrlParser ? transformUrlOptions : legacyTransformUrlOptions;
|
|
197
|
-
|
|
198
|
-
parseFn(url, options, (err, _object) => {
|
|
199
|
-
// Do not attempt to connect if parsing error
|
|
200
|
-
if (err) return callback(err);
|
|
201
|
-
|
|
202
|
-
// Flatten
|
|
203
|
-
const object = transform(_object);
|
|
204
|
-
|
|
205
|
-
// Parse the string
|
|
206
|
-
const _finalOptions = createUnifiedOptions(object, options);
|
|
207
|
-
|
|
208
|
-
// Check if we have connection and socket timeout set
|
|
209
|
-
if (_finalOptions.socketTimeoutMS == null) _finalOptions.socketTimeoutMS = 360000;
|
|
210
|
-
if (_finalOptions.connectTimeoutMS == null) _finalOptions.connectTimeoutMS = 30000;
|
|
211
|
-
if (_finalOptions.retryWrites == null) _finalOptions.retryWrites = true;
|
|
212
|
-
|
|
213
|
-
if (_finalOptions.db_options && _finalOptions.db_options.auth) {
|
|
214
|
-
delete _finalOptions.db_options.auth;
|
|
215
|
-
}
|
|
216
|
-
|
|
217
|
-
// Store the merged options object
|
|
218
|
-
mongoClient.s.options = _finalOptions;
|
|
219
|
-
|
|
220
|
-
// Failure modes
|
|
221
|
-
if (object.servers.length === 0) {
|
|
222
|
-
return callback(new Error('connection string must contain at least one seed host'));
|
|
223
|
-
}
|
|
224
|
-
|
|
225
|
-
if (_finalOptions.auth && !_finalOptions.credentials) {
|
|
226
|
-
try {
|
|
227
|
-
didRequestAuthentication = true;
|
|
228
|
-
_finalOptions.credentials = generateCredentials(
|
|
229
|
-
mongoClient,
|
|
230
|
-
_finalOptions.auth.user,
|
|
231
|
-
_finalOptions.auth.password,
|
|
232
|
-
_finalOptions
|
|
233
|
-
);
|
|
234
|
-
} catch (err) {
|
|
235
|
-
return callback(err);
|
|
236
|
-
}
|
|
237
|
-
}
|
|
238
|
-
|
|
239
|
-
if (_finalOptions.useUnifiedTopology) {
|
|
240
|
-
return createTopology(mongoClient, 'unified', _finalOptions, connectCallback);
|
|
241
|
-
}
|
|
242
|
-
|
|
243
|
-
// Do we have a replicaset then skip discovery and go straight to connectivity
|
|
244
|
-
if (_finalOptions.replicaSet || _finalOptions.rs_name) {
|
|
245
|
-
return createTopology(mongoClient, 'replicaset', _finalOptions, connectCallback);
|
|
246
|
-
} else if (object.servers.length > 1) {
|
|
247
|
-
return createTopology(mongoClient, 'mongos', _finalOptions, connectCallback);
|
|
248
|
-
} else {
|
|
249
|
-
return createServer(mongoClient, _finalOptions, connectCallback);
|
|
250
|
-
}
|
|
251
|
-
});
|
|
252
|
-
|
|
253
|
-
function connectCallback(err, topology) {
|
|
254
|
-
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`;
|
|
255
|
-
if (err && err.message === 'no mongos proxies found in seed list') {
|
|
256
|
-
if (logger.isWarn()) {
|
|
257
|
-
logger.warn(warningMessage);
|
|
258
|
-
}
|
|
259
|
-
|
|
260
|
-
// Return a more specific error message for MongoClient.connect
|
|
261
|
-
return callback(new MongoError(warningMessage));
|
|
262
|
-
}
|
|
263
|
-
|
|
264
|
-
if (didRequestAuthentication) {
|
|
265
|
-
mongoClient.emit('authenticated', null, true);
|
|
266
|
-
}
|
|
267
|
-
|
|
268
|
-
// Return the error and db instance
|
|
269
|
-
callback(err, topology);
|
|
270
|
-
}
|
|
271
|
-
}
|
|
272
|
-
|
|
273
|
-
/**
|
|
274
|
-
* Connect to MongoDB using a url as documented at
|
|
275
|
-
*
|
|
276
|
-
* docs.mongodb.org/manual/reference/connection-string/
|
|
277
|
-
*
|
|
278
|
-
* Note that for replicasets the replicaSet query parameter is required in the 2.0 driver
|
|
279
|
-
*
|
|
280
|
-
* @method
|
|
281
|
-
* @param {MongoClient} mongoClient The MongoClient instance with which to connect.
|
|
282
|
-
* @param {MongoClient~connectCallback} [callback] The command result callback
|
|
283
|
-
*/
|
|
284
|
-
function connectOp(mongoClient, err, callback) {
|
|
285
|
-
// Did we have a validation error
|
|
286
|
-
if (err) return callback(err);
|
|
287
|
-
// Fallback to callback based connect
|
|
288
|
-
connect(mongoClient, mongoClient.s.url, mongoClient.s.options, err => {
|
|
289
|
-
if (err) return callback(err);
|
|
290
|
-
callback(null, mongoClient);
|
|
291
|
-
});
|
|
292
|
-
}
|
|
293
|
-
|
|
294
|
-
function connectWithUrl(mongoClient, url, options, connectCallback) {
|
|
295
|
-
// Set the topology
|
|
296
|
-
assignTopology(mongoClient, url);
|
|
297
|
-
|
|
298
|
-
// Add listeners
|
|
299
|
-
addListeners(mongoClient, url);
|
|
300
|
-
|
|
301
|
-
// Propagate the events to the client
|
|
302
|
-
relayEvents(mongoClient, url);
|
|
303
|
-
|
|
304
|
-
let finalOptions = Object.assign({}, options);
|
|
305
|
-
|
|
306
|
-
// If we have a readPreference passed in by the db options, convert it from a string
|
|
307
|
-
if (typeof options.readPreference === 'string' || typeof options.read_preference === 'string') {
|
|
308
|
-
finalOptions.readPreference = new ReadPreference(
|
|
309
|
-
options.readPreference || options.read_preference
|
|
310
|
-
);
|
|
311
|
-
}
|
|
312
|
-
|
|
313
|
-
const isDoingAuth = finalOptions.user || finalOptions.password || finalOptions.authMechanism;
|
|
314
|
-
if (isDoingAuth && !finalOptions.credentials) {
|
|
315
|
-
try {
|
|
316
|
-
finalOptions.credentials = generateCredentials(
|
|
317
|
-
mongoClient,
|
|
318
|
-
finalOptions.user,
|
|
319
|
-
finalOptions.password,
|
|
320
|
-
finalOptions
|
|
321
|
-
);
|
|
322
|
-
} catch (err) {
|
|
323
|
-
return connectCallback(err, url);
|
|
324
|
-
}
|
|
325
|
-
}
|
|
326
|
-
|
|
327
|
-
return url.connect(finalOptions, connectCallback);
|
|
328
|
-
}
|
|
329
|
-
|
|
330
|
-
function createListener(mongoClient, event) {
|
|
331
|
-
const eventSet = new Set(['all', 'fullsetup', 'open', 'reconnect']);
|
|
332
|
-
return (v1, v2) => {
|
|
333
|
-
if (eventSet.has(event)) {
|
|
334
|
-
return mongoClient.emit(event, mongoClient);
|
|
335
|
-
}
|
|
336
|
-
|
|
337
|
-
mongoClient.emit(event, v1, v2);
|
|
338
|
-
};
|
|
339
|
-
}
|
|
340
|
-
|
|
341
|
-
function createServer(mongoClient, options, callback) {
|
|
342
|
-
// Pass in the promise library
|
|
343
|
-
options.promiseLibrary = mongoClient.s.promiseLibrary;
|
|
344
|
-
|
|
345
|
-
// Set default options
|
|
346
|
-
const servers = translateOptions(options);
|
|
347
|
-
|
|
348
|
-
const server = servers[0];
|
|
349
|
-
|
|
350
|
-
// Propagate the events to the client
|
|
351
|
-
const collectedEvents = collectEvents(mongoClient, server);
|
|
352
|
-
|
|
353
|
-
// Connect to topology
|
|
354
|
-
server.connect(options, (err, topology) => {
|
|
355
|
-
if (err) {
|
|
356
|
-
server.close(true);
|
|
357
|
-
return callback(err);
|
|
358
|
-
}
|
|
359
|
-
// Clear out all the collected event listeners
|
|
360
|
-
clearAllEvents(server);
|
|
361
|
-
|
|
362
|
-
// Relay all the events
|
|
363
|
-
relayEvents(mongoClient, server);
|
|
364
|
-
// Add listeners
|
|
365
|
-
addListeners(mongoClient, server);
|
|
366
|
-
// Check if we are really speaking to a mongos
|
|
367
|
-
const ismaster = topology.lastIsMaster();
|
|
368
|
-
|
|
369
|
-
// Set the topology
|
|
370
|
-
assignTopology(mongoClient, topology);
|
|
371
|
-
|
|
372
|
-
// Do we actually have a mongos
|
|
373
|
-
if (ismaster && ismaster.msg === 'isdbgrid') {
|
|
374
|
-
// Destroy the current connection
|
|
375
|
-
topology.close();
|
|
376
|
-
// Create mongos connection instead
|
|
377
|
-
return createTopology(mongoClient, 'mongos', options, callback);
|
|
378
|
-
}
|
|
379
|
-
|
|
380
|
-
// Fire all the events
|
|
381
|
-
replayEvents(mongoClient, collectedEvents);
|
|
382
|
-
// Otherwise callback
|
|
383
|
-
callback(err, topology);
|
|
384
|
-
});
|
|
385
|
-
}
|
|
386
|
-
|
|
387
|
-
function createTopology(mongoClient, topologyType, options, callback) {
|
|
388
|
-
// Pass in the promise library
|
|
389
|
-
options.promiseLibrary = mongoClient.s.promiseLibrary;
|
|
390
|
-
|
|
391
|
-
const translationOptions = {};
|
|
392
|
-
if (topologyType === 'unified') translationOptions.createServers = false;
|
|
393
|
-
|
|
394
|
-
// Set default options
|
|
395
|
-
const servers = translateOptions(options, translationOptions);
|
|
396
|
-
|
|
397
|
-
// Create the topology
|
|
398
|
-
let topology;
|
|
399
|
-
if (topologyType === 'mongos') {
|
|
400
|
-
topology = new Mongos(servers, options);
|
|
401
|
-
} else if (topologyType === 'replicaset') {
|
|
402
|
-
topology = new ReplSet(servers, options);
|
|
403
|
-
} else if (topologyType === 'unified') {
|
|
404
|
-
topology = new NativeTopology(options.servers, options);
|
|
405
|
-
}
|
|
406
|
-
|
|
407
|
-
// Add listeners
|
|
408
|
-
addListeners(mongoClient, topology);
|
|
409
|
-
|
|
410
|
-
// Propagate the events to the client
|
|
411
|
-
relayEvents(mongoClient, topology);
|
|
412
|
-
|
|
413
|
-
// Open the connection
|
|
414
|
-
topology.connect(options, (err, newTopology) => {
|
|
415
|
-
if (err) {
|
|
416
|
-
topology.close(true);
|
|
417
|
-
return callback(err);
|
|
418
|
-
}
|
|
419
|
-
|
|
420
|
-
assignTopology(mongoClient, newTopology);
|
|
421
|
-
if (options.autoEncryption == null) {
|
|
422
|
-
callback(null, newTopology);
|
|
423
|
-
return;
|
|
424
|
-
}
|
|
425
|
-
|
|
426
|
-
// setup for client side encryption
|
|
427
|
-
let AutoEncrypter;
|
|
428
|
-
try {
|
|
429
|
-
AutoEncrypter = require('mongodb-client-encryption').AutoEncrypter;
|
|
430
|
-
} catch (err) {
|
|
431
|
-
callback(
|
|
432
|
-
new MongoError(
|
|
433
|
-
'Auto-encryption requested, but the module is not installed. Please add `mongodb-client-encryption` as a dependency of your project'
|
|
434
|
-
)
|
|
435
|
-
);
|
|
436
|
-
|
|
437
|
-
return;
|
|
438
|
-
}
|
|
439
|
-
|
|
440
|
-
const MongoClient = loadClient();
|
|
441
|
-
const connectionString =
|
|
442
|
-
os.platform() === 'win32'
|
|
443
|
-
? 'mongodb://localhost:27020/?serverSelectionTimeoutMS=1000'
|
|
444
|
-
: 'mongodb://%2Ftmp%2Fmongocryptd.sock/?serverSelectionTimeoutMS=1000';
|
|
445
|
-
|
|
446
|
-
const mongocryptdClient = new MongoClient(connectionString, { useUnifiedTopology: true });
|
|
447
|
-
mongocryptdClient.connect(err => {
|
|
448
|
-
if (err) return callback(err, null);
|
|
449
|
-
|
|
450
|
-
const mongoCryptOptions = Object.assign({}, options.autoEncryption, {
|
|
451
|
-
mongocryptdClient
|
|
452
|
-
});
|
|
453
|
-
|
|
454
|
-
topology.s.options.autoEncrypter = new AutoEncrypter(mongoClient, mongoCryptOptions);
|
|
455
|
-
callback(null, newTopology);
|
|
456
|
-
});
|
|
457
|
-
});
|
|
458
|
-
}
|
|
459
|
-
|
|
460
|
-
function createUnifiedOptions(finalOptions, options) {
|
|
461
|
-
const childOptions = [
|
|
462
|
-
'mongos',
|
|
463
|
-
'server',
|
|
464
|
-
'db',
|
|
465
|
-
'replset',
|
|
466
|
-
'db_options',
|
|
467
|
-
'server_options',
|
|
468
|
-
'rs_options',
|
|
469
|
-
'mongos_options'
|
|
470
|
-
];
|
|
471
|
-
const noMerge = ['readconcern', 'compression'];
|
|
472
|
-
|
|
473
|
-
for (const name in options) {
|
|
474
|
-
if (noMerge.indexOf(name.toLowerCase()) !== -1) {
|
|
475
|
-
finalOptions[name] = options[name];
|
|
476
|
-
} else if (childOptions.indexOf(name.toLowerCase()) !== -1) {
|
|
477
|
-
finalOptions = mergeOptions(finalOptions, options[name], false);
|
|
478
|
-
} else {
|
|
479
|
-
if (
|
|
480
|
-
options[name] &&
|
|
481
|
-
typeof options[name] === 'object' &&
|
|
482
|
-
!Buffer.isBuffer(options[name]) &&
|
|
483
|
-
!Array.isArray(options[name])
|
|
484
|
-
) {
|
|
485
|
-
finalOptions = mergeOptions(finalOptions, options[name], true);
|
|
486
|
-
} else {
|
|
487
|
-
finalOptions[name] = options[name];
|
|
488
|
-
}
|
|
489
|
-
}
|
|
490
|
-
}
|
|
491
|
-
|
|
492
|
-
return finalOptions;
|
|
493
|
-
}
|
|
494
|
-
|
|
495
|
-
function legacyTransformUrlOptions(object) {
|
|
496
|
-
return mergeOptions(createUnifiedOptions({}, object), object, false);
|
|
497
|
-
}
|
|
498
|
-
|
|
499
|
-
function mergeOptions(target, source, flatten) {
|
|
500
|
-
for (const name in source) {
|
|
501
|
-
if (source[name] && typeof source[name] === 'object' && flatten) {
|
|
502
|
-
target = mergeOptions(target, source[name], flatten);
|
|
503
|
-
} else {
|
|
504
|
-
target[name] = source[name];
|
|
505
|
-
}
|
|
506
|
-
}
|
|
507
|
-
|
|
508
|
-
return target;
|
|
509
|
-
}
|
|
510
|
-
|
|
511
|
-
function relayEvents(mongoClient, topology) {
|
|
512
|
-
const serverOrCommandEvents = [
|
|
513
|
-
'serverOpening',
|
|
514
|
-
'serverDescriptionChanged',
|
|
515
|
-
'serverHeartbeatStarted',
|
|
516
|
-
'serverHeartbeatSucceeded',
|
|
517
|
-
'serverHeartbeatFailed',
|
|
518
|
-
'serverClosed',
|
|
519
|
-
'topologyOpening',
|
|
520
|
-
'topologyClosed',
|
|
521
|
-
'topologyDescriptionChanged',
|
|
522
|
-
'commandStarted',
|
|
523
|
-
'commandSucceeded',
|
|
524
|
-
'commandFailed',
|
|
525
|
-
'joined',
|
|
526
|
-
'left',
|
|
527
|
-
'ping',
|
|
528
|
-
'ha'
|
|
529
|
-
];
|
|
530
|
-
|
|
531
|
-
serverOrCommandEvents.forEach(event => {
|
|
532
|
-
topology.on(event, (object1, object2) => {
|
|
533
|
-
mongoClient.emit(event, object1, object2);
|
|
534
|
-
});
|
|
535
|
-
});
|
|
536
|
-
}
|
|
537
|
-
|
|
538
|
-
//
|
|
539
|
-
// Replay any events due to single server connection switching to Mongos
|
|
540
|
-
//
|
|
541
|
-
function replayEvents(mongoClient, events) {
|
|
542
|
-
for (let i = 0; i < events.length; i++) {
|
|
543
|
-
mongoClient.emit(events[i].event, events[i].object1, events[i].object2);
|
|
544
|
-
}
|
|
545
|
-
}
|
|
546
|
-
|
|
547
|
-
const LEGACY_OPTIONS_MAP = validOptionNames.reduce((obj, name) => {
|
|
548
|
-
obj[name.toLowerCase()] = name;
|
|
549
|
-
return obj;
|
|
550
|
-
}, {});
|
|
551
|
-
|
|
552
|
-
function transformUrlOptions(_object) {
|
|
553
|
-
let object = Object.assign({ servers: _object.hosts }, _object.options);
|
|
554
|
-
for (let name in object) {
|
|
555
|
-
const camelCaseName = LEGACY_OPTIONS_MAP[name];
|
|
556
|
-
if (camelCaseName) {
|
|
557
|
-
object[camelCaseName] = object[name];
|
|
558
|
-
}
|
|
559
|
-
}
|
|
560
|
-
|
|
561
|
-
const hasUsername = _object.auth && _object.auth.username;
|
|
562
|
-
const hasAuthMechanism = _object.options && _object.options.authMechanism;
|
|
563
|
-
if (hasUsername || hasAuthMechanism) {
|
|
564
|
-
object.auth = Object.assign({}, _object.auth);
|
|
565
|
-
if (object.auth.db) {
|
|
566
|
-
object.authSource = object.authSource || object.auth.db;
|
|
567
|
-
}
|
|
568
|
-
|
|
569
|
-
if (object.auth.username) {
|
|
570
|
-
object.auth.user = object.auth.username;
|
|
571
|
-
}
|
|
572
|
-
}
|
|
573
|
-
|
|
574
|
-
if (_object.defaultDatabase) {
|
|
575
|
-
object.dbName = _object.defaultDatabase;
|
|
576
|
-
}
|
|
577
|
-
|
|
578
|
-
if (object.maxpoolsize) {
|
|
579
|
-
object.poolSize = object.maxpoolsize;
|
|
580
|
-
}
|
|
581
|
-
|
|
582
|
-
if (object.readconcernlevel) {
|
|
583
|
-
object.readConcern = new ReadConcern(object.readconcernlevel);
|
|
584
|
-
}
|
|
585
|
-
|
|
586
|
-
if (object.wtimeoutms) {
|
|
587
|
-
object.wtimeout = object.wtimeoutms;
|
|
588
|
-
}
|
|
589
|
-
|
|
590
|
-
if (_object.srvHost) {
|
|
591
|
-
object.srvHost = _object.srvHost;
|
|
592
|
-
}
|
|
593
|
-
|
|
594
|
-
return object;
|
|
595
|
-
}
|
|
596
|
-
|
|
597
|
-
function translateOptions(options, translationOptions) {
|
|
598
|
-
translationOptions = Object.assign({}, { createServers: true }, translationOptions);
|
|
599
|
-
|
|
600
|
-
// If we have a readPreference passed in by the db options
|
|
601
|
-
if (typeof options.readPreference === 'string' || typeof options.read_preference === 'string') {
|
|
602
|
-
options.readPreference = new ReadPreference(options.readPreference || options.read_preference);
|
|
603
|
-
}
|
|
604
|
-
|
|
605
|
-
// Do we have readPreference tags, add them
|
|
606
|
-
if (options.readPreference && (options.readPreferenceTags || options.read_preference_tags)) {
|
|
607
|
-
options.readPreference.tags = options.readPreferenceTags || options.read_preference_tags;
|
|
608
|
-
}
|
|
609
|
-
|
|
610
|
-
// Do we have maxStalenessSeconds
|
|
611
|
-
if (options.maxStalenessSeconds) {
|
|
612
|
-
options.readPreference.maxStalenessSeconds = options.maxStalenessSeconds;
|
|
613
|
-
}
|
|
614
|
-
|
|
615
|
-
// Set the socket and connection timeouts
|
|
616
|
-
if (options.socketTimeoutMS == null) options.socketTimeoutMS = 360000;
|
|
617
|
-
if (options.connectTimeoutMS == null) options.connectTimeoutMS = 30000;
|
|
618
|
-
|
|
619
|
-
if (!translationOptions.createServers) {
|
|
620
|
-
return;
|
|
621
|
-
}
|
|
622
|
-
|
|
623
|
-
// Create server instances
|
|
624
|
-
return options.servers.map(serverObj => {
|
|
625
|
-
return serverObj.domain_socket
|
|
626
|
-
? new Server(serverObj.domain_socket, 27017, options)
|
|
627
|
-
: new Server(serverObj.host, serverObj.port, options);
|
|
628
|
-
});
|
|
629
|
-
}
|
|
630
|
-
|
|
631
|
-
// Validate options object
|
|
632
|
-
function validOptions(options) {
|
|
633
|
-
const _validOptions = validOptionNames.concat(legacyOptionNames);
|
|
634
|
-
|
|
635
|
-
for (const name in options) {
|
|
636
|
-
if (ignoreOptionNames.indexOf(name) !== -1) {
|
|
637
|
-
continue;
|
|
638
|
-
}
|
|
639
|
-
|
|
640
|
-
if (_validOptions.indexOf(name) === -1) {
|
|
641
|
-
if (options.validateOptions) {
|
|
642
|
-
return new MongoError(`option ${name} is not supported`);
|
|
643
|
-
} else {
|
|
644
|
-
console.warn(`the options [${name}] is not supported`);
|
|
645
|
-
}
|
|
646
|
-
}
|
|
647
|
-
|
|
648
|
-
if (legacyOptionNames.indexOf(name) !== -1) {
|
|
649
|
-
console.warn(
|
|
650
|
-
`the server/replset/mongos/db options are deprecated, ` +
|
|
651
|
-
`all their options are supported at the top level of the options object [${validOptionNames}]`
|
|
652
|
-
);
|
|
653
|
-
}
|
|
654
|
-
}
|
|
655
|
-
}
|
|
656
|
-
|
|
657
|
-
const VALID_AUTH_MECHANISMS = new Set([
|
|
658
|
-
'DEFAULT',
|
|
659
|
-
'MONGODB-CR',
|
|
660
|
-
'PLAIN',
|
|
661
|
-
'MONGODB-X509',
|
|
662
|
-
'SCRAM-SHA-1',
|
|
663
|
-
'SCRAM-SHA-256',
|
|
664
|
-
'GSSAPI'
|
|
665
|
-
]);
|
|
666
|
-
|
|
667
|
-
const AUTH_MECHANISM_INTERNAL_MAP = {
|
|
668
|
-
DEFAULT: 'default',
|
|
669
|
-
'MONGODB-CR': 'mongocr',
|
|
670
|
-
PLAIN: 'plain',
|
|
671
|
-
'MONGODB-X509': 'x509',
|
|
672
|
-
'SCRAM-SHA-1': 'scram-sha-1',
|
|
673
|
-
'SCRAM-SHA-256': 'scram-sha-256'
|
|
674
|
-
};
|
|
675
|
-
|
|
676
|
-
function generateCredentials(client, username, password, options) {
|
|
677
|
-
options = Object.assign({}, options);
|
|
678
|
-
|
|
679
|
-
// the default db to authenticate against is 'self'
|
|
680
|
-
// if authenticate is called from a retry context, it may be another one, like admin
|
|
681
|
-
const source = options.authSource || options.authdb || options.dbName;
|
|
682
|
-
|
|
683
|
-
// authMechanism
|
|
684
|
-
const authMechanismRaw = options.authMechanism || 'DEFAULT';
|
|
685
|
-
const authMechanism = authMechanismRaw.toUpperCase();
|
|
686
|
-
|
|
687
|
-
if (!VALID_AUTH_MECHANISMS.has(authMechanism)) {
|
|
688
|
-
throw MongoError.create({
|
|
689
|
-
message: `authentication mechanism ${authMechanismRaw} not supported', options.authMechanism`,
|
|
690
|
-
driver: true
|
|
691
|
-
});
|
|
692
|
-
}
|
|
693
|
-
|
|
694
|
-
if (authMechanism === 'GSSAPI') {
|
|
695
|
-
return new MongoCredentials({
|
|
696
|
-
mechanism: process.platform === 'win32' ? 'sspi' : 'gssapi',
|
|
697
|
-
mechanismProperties: options,
|
|
698
|
-
source,
|
|
699
|
-
username,
|
|
700
|
-
password
|
|
701
|
-
});
|
|
702
|
-
}
|
|
703
|
-
|
|
704
|
-
return new MongoCredentials({
|
|
705
|
-
mechanism: AUTH_MECHANISM_INTERNAL_MAP[authMechanism],
|
|
706
|
-
source,
|
|
707
|
-
username,
|
|
708
|
-
password
|
|
709
|
-
});
|
|
710
|
-
}
|
|
711
|
-
|
|
712
|
-
function closeOperation(client, force, callback) {
|
|
713
|
-
const completeClose = err => {
|
|
714
|
-
client.emit('close', client);
|
|
715
|
-
for (const name in client.s.dbCache) {
|
|
716
|
-
client.s.dbCache[name].emit('close', client);
|
|
717
|
-
}
|
|
718
|
-
|
|
719
|
-
client.removeAllListeners('close');
|
|
720
|
-
callback(err, null);
|
|
721
|
-
};
|
|
722
|
-
|
|
723
|
-
if (client.topology == null) {
|
|
724
|
-
completeClose();
|
|
725
|
-
return;
|
|
726
|
-
}
|
|
727
|
-
|
|
728
|
-
client.topology.close(force, completeClose);
|
|
729
|
-
}
|
|
730
|
-
|
|
731
|
-
module.exports = { connectOp, validOptions, closeOperation };
|