mongodb 3.6.4 → 3.6.5

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/HISTORY.md CHANGED
@@ -2,6 +2,16 @@
2
2
 
3
3
  All notable changes to this project will be documented in this file. See [standard-version](https://github.com/conventional-changelog/standard-version) for commit guidelines.
4
4
 
5
+ ### [3.6.5](https://github.com/mongodb/node-mongodb-native/compare/v3.6.4...v3.6.5) (2021-03-16)
6
+
7
+
8
+ ### Bug Fixes
9
+
10
+ * MongoError circular dependency warning ([#2734](https://github.com/mongodb/node-mongodb-native/issues/2734)) ([d67ffa7](https://github.com/mongodb/node-mongodb-native/commit/d67ffa7a2e3f86734c7e9b6944aab1d765b9e75e))
11
+ * move session support check to operation layer ([#2739](https://github.com/mongodb/node-mongodb-native/issues/2739)) ([8b370a7](https://github.com/mongodb/node-mongodb-native/commit/8b370a7ad784f5759c964cdfaec62e06c896dc95))
12
+ * session support detection spec compliance ([#2732](https://github.com/mongodb/node-mongodb-native/issues/2732)) ([9baec71](https://github.com/mongodb/node-mongodb-native/commit/9baec7128f612f2d9c290c85d24e33602f911499))
13
+ * use emitWarning API for internal messages ([#2743](https://github.com/mongodb/node-mongodb-native/issues/2743)) ([8bd9777](https://github.com/mongodb/node-mongodb-native/commit/8bd9777b0aedd56b81675c3e79fae63432319982))
14
+
5
15
  ### [3.6.4](https://github.com/mongodb/node-mongodb-native/compare/v3.6.3...v3.6.4) (2021-02-02)
6
16
 
7
17
 
package/README.md CHANGED
@@ -1,21 +1,24 @@
1
- [![npm](https://nodei.co/npm/mongodb.png?downloads=true&downloadRank=true)](https://nodei.co/npm/mongodb/) [![npm](https://nodei.co/npm-dl/mongodb.png?months=6&height=3)](https://nodei.co/npm/mongodb/)
1
+ # MongoDB NodeJS Driver
2
2
 
3
- [![Gitter](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/mongodb/node-mongodb-native?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge)
3
+ [![npm](https://nodei.co/npm/mongodb.png?downloads=true&downloadRank=true)](https://nodei.co/npm/mongodb/)
4
4
 
5
- # Description
5
+ The official [MongoDB](https://www.mongodb.com/) driver for Node.js.
6
6
 
7
- The official [MongoDB](https://www.mongodb.com/) driver for Node.js. Provides a high-level API on top of [mongodb-core](https://www.npmjs.com/package/mongodb-core) that is meant for end users.
7
+ **NOTE: v3.x released with breaking API changes. You can find a list of changes [here](CHANGES_3.0.0.md).**
8
8
 
9
- **NOTE: v3.x was recently released with breaking API changes. You can find a list of changes [here](CHANGES_3.0.0.md).**
9
+ ## Version 4.0
10
10
 
11
- ## MongoDB Node.JS Driver
11
+ **Looking for the latest?** We're working on the next major version of the driver, now in beta.
12
+ Check out our [beta version 4.0 here](https://github.com/mongodb/node-mongodb-native/tree/4.0), which includes a full migration of the driver to TypeScript.
12
13
 
13
- | what | where |
14
- |---------------|------------------------------------------------|
15
- | documentation | http://mongodb.github.io/node-mongodb-native |
16
- | api-doc | http://mongodb.github.io/node-mongodb-native/3.6/api |
17
- | source | https://github.com/mongodb/node-mongodb-native |
18
- | mongodb | http://www.mongodb.org |
14
+ ## Quick Links
15
+
16
+ | what | where |
17
+ | ------------- | ---------------------------------------------------- |
18
+ | documentation | http://mongodb.github.io/node-mongodb-native |
19
+ | api-doc | http://mongodb.github.io/node-mongodb-native/3.6/api |
20
+ | source | https://github.com/mongodb/node-mongodb-native |
21
+ | mongodb | http://www.mongodb.org |
19
22
 
20
23
  ### Bugs / Feature Requests
21
24
 
@@ -41,12 +44,12 @@ Change history can be found in [`HISTORY.md`](HISTORY.md).
41
44
 
42
45
  For version compatibility matrices, please refer to the following links:
43
46
 
44
- * [MongoDB](https://docs.mongodb.com/ecosystem/drivers/driver-compatibility-reference/#reference-compatibility-mongodb-node)
45
- * [NodeJS](https://docs.mongodb.com/ecosystem/drivers/driver-compatibility-reference/#reference-compatibility-language-node)
47
+ - [MongoDB](https://docs.mongodb.com/ecosystem/drivers/driver-compatibility-reference/#reference-compatibility-mongodb-node)
48
+ - [NodeJS](https://docs.mongodb.com/ecosystem/drivers/driver-compatibility-reference/#reference-compatibility-language-node)
46
49
 
47
- # Installation
50
+ ## Installation
48
51
 
49
- The recommended way to get started using the Node.js 3.0 driver is by using the `npm` (Node Package Manager) to install the dependency in your project.
52
+ The recommended way to get started using the Node.js driver is by using `npm` (Node Package Manager) to install the dependency in your project.
50
53
 
51
54
  ## MongoDB Driver
52
55
 
@@ -64,10 +67,10 @@ You can also use the [Yarn](https://yarnpkg.com/en) package manager.
64
67
 
65
68
  The MongoDB driver depends on several other packages. These are:
66
69
 
67
- * [mongodb-core](https://github.com/mongodb-js/mongodb-core)
68
- * [bson](https://github.com/mongodb/js-bson)
69
- * [kerberos](https://github.com/mongodb-js/kerberos)
70
- * [node-gyp](https://github.com/nodejs/node-gyp)
70
+ - [bson](https://github.com/mongodb/js-bson)
71
+ - [bson-ext](https://github.com/mongodb-js/bson-ext)
72
+ - [kerberos](https://github.com/mongodb-js/kerberos)
73
+ - [mongodb-client-encryption](https://github.com/mongodb/libmongocrypt#readme)
71
74
 
72
75
  The `kerberos` package is a C++ extension that requires a build environment to be installed on your system. You must be able to build Node.js itself in order to compile and install the `kerberos` module. Furthermore, the `kerberos` module requires the MIT Kerberos package to correctly compile on UNIX operating systems. Consult your UNIX operation system package manager for what libraries to install.
73
76
 
@@ -108,9 +111,9 @@ This will print out all the steps npm is performing while trying to install the
108
111
 
109
112
  A compiler tool chain known to work for compiling `kerberos` on Windows is the following.
110
113
 
111
- * Visual Studio C++ 2010 (do not use higher versions)
112
- * Windows 7 64bit SDK
113
- * Python 2.7 or higher
114
+ - Visual Studio C++ 2010 (do not use higher versions)
115
+ - Windows 7 64bit SDK
116
+ - Python 2.7 or higher
114
117
 
115
118
  Open the Visual Studio command prompt. Ensure `node.exe` is in your path and install `node-gyp`.
116
119
 
@@ -168,7 +171,7 @@ For complete MongoDB installation instructions, see [the manual](https://docs.mo
168
171
 
169
172
  1. Download the right MongoDB version from [MongoDB](https://www.mongodb.org/downloads)
170
173
  2. Create a database directory (in this case under **/data**).
171
- 3. Install and start a ``mongod`` process.
174
+ 3. Install and start a `mongod` process.
172
175
 
173
176
  ```bash
174
177
  mongod --dbpath=/data
@@ -192,11 +195,11 @@ const url = 'mongodb://localhost:27017';
192
195
 
193
196
  // Database Name
194
197
  const dbName = 'myproject';
195
-
198
+ const client = new MongoClient(url);
196
199
  // Use connect method to connect to the server
197
- MongoClient.connect(url, function(err, client) {
200
+ client.connect(function(err) {
198
201
  assert.equal(null, err);
199
- console.log("Connected successfully to server");
202
+ console.log('Connected successfully to server');
200
203
 
201
204
  const db = client.db(dbName);
202
205
 
@@ -222,23 +225,21 @@ const insertDocuments = function(db, callback) {
222
225
  // Get the documents collection
223
226
  const collection = db.collection('documents');
224
227
  // Insert some documents
225
- collection.insertMany([
226
- {a : 1}, {a : 2}, {a : 3}
227
- ], function(err, result) {
228
+ collection.insertMany([{ a: 1 }, { a: 2 }, { a: 3 }], function(err, result) {
228
229
  assert.equal(err, null);
229
230
  assert.equal(3, result.result.n);
230
231
  assert.equal(3, result.ops.length);
231
- console.log("Inserted 3 documents into the collection");
232
+ console.log('Inserted 3 documents into the collection');
232
233
  callback(result);
233
234
  });
234
- }
235
+ };
235
236
  ```
236
237
 
237
238
  The **insert** command returns an object with the following fields:
238
239
 
239
- * **result** Contains the result document from MongoDB
240
- * **ops** Contains the documents inserted with added **_id** fields
241
- * **connection** Contains the connection used to perform the insert
240
+ - **result** Contains the result document from MongoDB
241
+ - **ops** Contains the documents inserted with added **\_id** fields
242
+ - **connection** Contains the connection used to perform the insert
242
243
 
243
244
  Add the following code to call the **insertDocuments** function:
244
245
 
@@ -255,7 +256,7 @@ const dbName = 'myproject';
255
256
  // Use connect method to connect to the server
256
257
  MongoClient.connect(url, function(err, client) {
257
258
  assert.equal(null, err);
258
- console.log("Connected successfully to server");
259
+ console.log('Connected successfully to server');
259
260
 
260
261
  const db = client.db(dbName);
261
262
 
@@ -289,11 +290,11 @@ const findDocuments = function(db, callback) {
289
290
  // Find some documents
290
291
  collection.find({}).toArray(function(err, docs) {
291
292
  assert.equal(err, null);
292
- console.log("Found the following records");
293
- console.log(docs)
293
+ console.log('Found the following records');
294
+ console.log(docs);
294
295
  callback(docs);
295
296
  });
296
- }
297
+ };
297
298
  ```
298
299
 
299
300
  This query returns all the documents in the **documents** collection. Add the **findDocument** method to the **MongoClient.connect** callback:
@@ -311,7 +312,7 @@ const dbName = 'myproject';
311
312
  // Use connect method to connect to the server
312
313
  MongoClient.connect(url, function(err, client) {
313
314
  assert.equal(null, err);
314
- console.log("Connected correctly to server");
315
+ console.log('Connected correctly to server');
315
316
 
316
317
  const db = client.db(dbName);
317
318
 
@@ -332,16 +333,16 @@ const findDocuments = function(db, callback) {
332
333
  // Get the documents collection
333
334
  const collection = db.collection('documents');
334
335
  // Find some documents
335
- collection.find({'a': 3}).toArray(function(err, docs) {
336
+ collection.find({ a: 3 }).toArray(function(err, docs) {
336
337
  assert.equal(err, null);
337
- console.log("Found the following records");
338
+ console.log('Found the following records');
338
339
  console.log(docs);
339
340
  callback(docs);
340
341
  });
341
- }
342
+ };
342
343
  ```
343
344
 
344
- Only the documents which match ``'a' : 3`` should be returned.
345
+ Only the documents which match `'a' : 3` should be returned.
345
346
 
346
347
  ### Update a document
347
348
 
@@ -352,14 +353,13 @@ const updateDocument = function(db, callback) {
352
353
  // Get the documents collection
353
354
  const collection = db.collection('documents');
354
355
  // Update document where a is 2, set b equal to 1
355
- collection.updateOne({ a : 2 }
356
- , { $set: { b : 1 } }, function(err, result) {
356
+ collection.updateOne({ a: 2 }, { $set: { b: 1 } }, function(err, result) {
357
357
  assert.equal(err, null);
358
358
  assert.equal(1, result.result.n);
359
- console.log("Updated the document with the field a equal to 2");
359
+ console.log('Updated the document with the field a equal to 2');
360
360
  callback(result);
361
361
  });
362
- }
362
+ };
363
363
  ```
364
364
 
365
365
  The method updates the first document where the field **a** is equal to **2** by adding a new field **b** to the document set to **1**. Next, update the callback function from **MongoClient.connect** to include the update method.
@@ -377,7 +377,7 @@ const dbName = 'myproject';
377
377
  // Use connect method to connect to the server
378
378
  MongoClient.connect(url, function(err, client) {
379
379
  assert.equal(null, err);
380
- console.log("Connected successfully to server");
380
+ console.log('Connected successfully to server');
381
381
 
382
382
  const db = client.db(dbName);
383
383
 
@@ -398,13 +398,13 @@ const removeDocument = function(db, callback) {
398
398
  // Get the documents collection
399
399
  const collection = db.collection('documents');
400
400
  // Delete document where a is 3
401
- collection.deleteOne({ a : 3 }, function(err, result) {
401
+ collection.deleteOne({ a: 3 }, function(err, result) {
402
402
  assert.equal(err, null);
403
403
  assert.equal(1, result.result.n);
404
- console.log("Removed the document with the field a equal to 3");
404
+ console.log('Removed the document with the field a equal to 3');
405
405
  callback(result);
406
406
  });
407
- }
407
+ };
408
408
  ```
409
409
 
410
410
  Add the new method to the **MongoClient.connect** callback function.
@@ -422,7 +422,7 @@ const dbName = 'myproject';
422
422
  // Use connect method to connect to the server
423
423
  MongoClient.connect(url, function(err, client) {
424
424
  assert.equal(null, err);
425
- console.log("Connected successfully to server");
425
+ console.log('Connected successfully to server');
426
426
 
427
427
  const db = client.db(dbName);
428
428
 
@@ -444,18 +444,14 @@ performance. The following function creates an index on the **a** field in the
444
444
 
445
445
  ```js
446
446
  const indexCollection = function(db, callback) {
447
- db.collection('documents').createIndex(
448
- { "a": 1 },
449
- null,
450
- function(err, results) {
451
- console.log(results);
452
- callback();
453
- }
454
- );
447
+ db.collection('documents').createIndex({ a: 1 }, null, function(err, results) {
448
+ console.log(results);
449
+ callback();
450
+ });
455
451
  };
456
452
  ```
457
453
 
458
- Add the ``indexCollection`` method to your app:
454
+ Add the `indexCollection` method to your app:
459
455
 
460
456
  ```js
461
457
  const MongoClient = require('mongodb').MongoClient;
@@ -469,7 +465,7 @@ const dbName = 'myproject';
469
465
  // Use connect method to connect to the server
470
466
  MongoClient.connect(url, function(err, client) {
471
467
  assert.equal(null, err);
472
- console.log("Connected successfully to server");
468
+ console.log('Connected successfully to server');
473
469
 
474
470
  const db = client.db(dbName);
475
471
 
@@ -485,13 +481,13 @@ For more detailed information, see the [tutorials](docs/reference/content/tutori
485
481
 
486
482
  ## Next Steps
487
483
 
488
- * [MongoDB Documentation](http://mongodb.org)
489
- * [Read about Schemas](http://learnmongodbthehardway.com)
490
- * [Star us on GitHub](https://github.com/mongodb/node-mongodb-native)
484
+ - [MongoDB Documentation](http://mongodb.org)
485
+ - [Read about Schemas](http://learnmongodbthehardway.com)
486
+ - [Star us on GitHub](https://github.com/mongodb/node-mongodb-native)
491
487
 
492
488
  ## License
493
489
 
494
490
  [Apache 2.0](LICENSE.md)
495
491
 
496
- © 2009-2012 Christian Amor Kvalheim
492
+ © 2009-2012 Christian Amor Kvalheim
497
493
  © 2012-present MongoDB [Contributors](CONTRIBUTORS.md)
package/lib/collection.js CHANGED
@@ -2,6 +2,7 @@
2
2
 
3
3
  const deprecate = require('util').deprecate;
4
4
  const deprecateOptions = require('./utils').deprecateOptions;
5
+ const emitWarningOnce = require('./utils').emitWarningOnce;
5
6
  const checkCollectionName = require('./utils').checkCollectionName;
6
7
  const ObjectID = require('./core').BSON.ObjectID;
7
8
  const MongoError = require('./core').MongoError;
@@ -323,7 +324,7 @@ Collection.prototype.find = deprecateOptions(
323
324
  function(query, options, callback) {
324
325
  if (typeof callback === 'object') {
325
326
  // TODO(MAJOR): throw in the future
326
- console.warn('Third parameter to `find()` must be a callback or undefined');
327
+ emitWarningOnce('Third parameter to `find()` must be a callback or undefined');
327
328
  }
328
329
 
329
330
  let selector = query;
@@ -1092,7 +1093,7 @@ Collection.prototype.findOne = deprecateOptions(
1092
1093
  function(query, options, callback) {
1093
1094
  if (typeof callback === 'object') {
1094
1095
  // TODO(MAJOR): throw in the future
1095
- console.warn('Third parameter to `findOne()` must be a callback or undefined');
1096
+ emitWarningOnce('Third parameter to `findOne()` must be a callback or undefined');
1096
1097
  }
1097
1098
 
1098
1099
  if (typeof query === 'function') (callback = query), (query = {}), (options = {});
@@ -4,6 +4,7 @@ const Buffer = require('safe-buffer').Buffer;
4
4
  const retrieveBSON = require('../connection/utils').retrieveBSON;
5
5
  const MongoError = require('../error').MongoError;
6
6
  const AuthProvider = require('./auth_provider').AuthProvider;
7
+ const emitWarningOnce = require('../../utils').emitWarning;
7
8
 
8
9
  const BSON = retrieveBSON();
9
10
  const Binary = BSON.Binary;
@@ -24,7 +25,7 @@ class ScramSHA extends AuthProvider {
24
25
  prepare(handshakeDoc, authContext, callback) {
25
26
  const cryptoMethod = this.cryptoMethod;
26
27
  if (cryptoMethod === 'sha256' && saslprep == null) {
27
- console.warn('Warning: no saslprep library specified. Passwords will not be sanitized');
28
+ emitWarningOnce('Warning: no saslprep library specified. Passwords will not be sanitized');
28
29
  }
29
30
 
30
31
  crypto.randomBytes(24, (err, nonce) => {
@@ -37,6 +37,7 @@ var Logger = function(className, options) {
37
37
  if (options.logger) {
38
38
  currentLogger = options.logger;
39
39
  } else if (currentLogger == null) {
40
+ // eslint-disable-next-line no-console
40
41
  currentLogger = console.log;
41
42
  }
42
43
 
@@ -31,6 +31,7 @@ const Buffer = require('safe-buffer').Buffer;
31
31
  const opcodes = require('../wireprotocol/shared').opcodes;
32
32
  const databaseNamespace = require('../wireprotocol/shared').databaseNamespace;
33
33
  const ReadPreference = require('../topologies/read_preference');
34
+ const MongoError = require('../../core/error').MongoError;
34
35
 
35
36
  // Incrementing request id
36
37
  let _requestId = 0;
@@ -196,7 +197,8 @@ class BinMsg {
196
197
  while (this.index < this.data.length) {
197
198
  const payloadType = this.data.readUInt8(this.index++);
198
199
  if (payloadType === 1) {
199
- console.error('TYPE 1');
200
+ // It was decided that no driver makes use of payload type 1
201
+ throw new MongoError('OP_MSG Payload Type 1 detected unsupported protocol');
200
202
  } else if (payloadType === 0) {
201
203
  const bsonSize = this.data.readUInt32LE(this.index);
202
204
  const bin = this.data.slice(this.index, this.index + bsonSize);
@@ -27,6 +27,7 @@ const ServerSessionPool = require('../sessions').ServerSessionPool;
27
27
  const makeClientMetadata = require('../utils').makeClientMetadata;
28
28
  const CMAP_EVENT_NAMES = require('../../cmap/events').CMAP_EVENT_NAMES;
29
29
  const compareTopologyVersion = require('./server_description').compareTopologyVersion;
30
+ const emitWarning = require('../../utils').emitWarning;
30
31
 
31
32
  const common = require('./common');
32
33
  const drainTimerQueue = common.drainTimerQueue;
@@ -739,7 +740,7 @@ class Topology extends EventEmitter {
739
740
  }
740
741
 
741
742
  unref() {
742
- console.log('not implemented: `unref`');
743
+ emitWarning('not implemented: `unref`');
743
744
  }
744
745
 
745
746
  // NOTE: There are many places in code where we explicitly check the last isMaster
@@ -72,12 +72,30 @@ class TopologyDescription {
72
72
  // value among ServerDescriptions of all data-bearing server types. If any have a null
73
73
  // logicalSessionTimeoutMinutes, then TopologyDescription.logicalSessionTimeoutMinutes MUST be
74
74
  // set to null.
75
- const readableServers = Array.from(this.servers.values()).filter(s => s.isReadable);
76
- this.logicalSessionTimeoutMinutes = readableServers.reduce((result, server) => {
77
- if (server.logicalSessionTimeoutMinutes == null) return null;
78
- if (result == null) return server.logicalSessionTimeoutMinutes;
79
- return Math.min(result, server.logicalSessionTimeoutMinutes);
80
- }, null);
75
+ this.logicalSessionTimeoutMinutes = null;
76
+ for (const addressServerTuple of this.servers) {
77
+ const server = addressServerTuple[1];
78
+ if (server.isReadable) {
79
+ if (server.logicalSessionTimeoutMinutes == null) {
80
+ // If any of the servers have a null logicalSessionsTimeout, then the whole topology does
81
+ this.logicalSessionTimeoutMinutes = null;
82
+ break;
83
+ }
84
+
85
+ if (this.logicalSessionTimeoutMinutes == null) {
86
+ // First server with a non null logicalSessionsTimeout
87
+ this.logicalSessionTimeoutMinutes = server.logicalSessionTimeoutMinutes;
88
+ continue;
89
+ }
90
+
91
+ // Always select the smaller of the:
92
+ // current server logicalSessionsTimeout and the topologies logicalSessionsTimeout
93
+ this.logicalSessionTimeoutMinutes = Math.min(
94
+ this.logicalSessionTimeoutMinutes,
95
+ server.logicalSessionTimeoutMinutes
96
+ );
97
+ }
98
+ }
81
99
  }
82
100
 
83
101
  /**
@@ -52,6 +52,7 @@ exports.attachToRunner = function(runner, outputFile) {
52
52
  fs.writeFileSync(outputFile, JSON.stringify(smokeOutput));
53
53
 
54
54
  // Standard NodeJS uncaught exception handler
55
+ // eslint-disable-next-line no-console
55
56
  console.error(err.stack);
56
57
  process.exit(1);
57
58
  });
@@ -1,4 +1,5 @@
1
1
  'use strict';
2
+ const emitWarningOnce = require('../../utils').emitWarningOnce;
2
3
 
3
4
  /**
4
5
  * The **ReadPreference** class is a class that represents a MongoDB ReadPreference and is
@@ -20,7 +21,7 @@ const ReadPreference = function(mode, tags, options) {
20
21
 
21
22
  // TODO(major): tags MUST be an array of tagsets
22
23
  if (tags && !Array.isArray(tags)) {
23
- console.warn(
24
+ emitWarningOnce(
24
25
  'ReadPreference tags must be an array, this will change in the next major version'
25
26
  );
26
27
 
@@ -4,6 +4,7 @@ const qs = require('querystring');
4
4
  const dns = require('dns');
5
5
  const MongoParseError = require('./error').MongoParseError;
6
6
  const ReadPreference = require('./topologies/read_preference');
7
+ const emitWarningOnce = require('../utils').emitWarningOnce;
7
8
 
8
9
  /**
9
10
  * The following regular expression validates a connection string and breaks the
@@ -438,7 +439,7 @@ function parseQueryString(query, options) {
438
439
  // special cases for known deprecated options
439
440
  if (result.wtimeout && result.wtimeoutms) {
440
441
  delete result.wtimeout;
441
- console.warn('Unsupported option `wtimeout` specified');
442
+ emitWarningOnce('Unsupported option `wtimeout` specified');
442
443
  }
443
444
 
444
445
  return Object.keys(result).length ? result : null;
@@ -5,6 +5,7 @@ const MongoError = require('../error').MongoError;
5
5
  const MongoNetworkError = require('../error').MongoNetworkError;
6
6
  const collectionNamespace = require('./shared').collectionNamespace;
7
7
  const maxWireVersion = require('../utils').maxWireVersion;
8
+ const emitWarning = require('../utils').emitWarning;
8
9
  const command = require('./command');
9
10
 
10
11
  function killCursors(server, ns, cursorState, callback) {
@@ -31,7 +32,7 @@ function killCursors(server, ns, cursorState, callback) {
31
32
  if (typeof callback === 'function') {
32
33
  callback(err, null);
33
34
  } else {
34
- console.warn(err);
35
+ emitWarning(err);
35
36
  }
36
37
  }
37
38
  }
package/lib/db.js CHANGED
@@ -12,7 +12,7 @@ const MongoError = require('./core').MongoError;
12
12
  const ObjectID = require('./core').ObjectID;
13
13
  const Logger = require('./core').Logger;
14
14
  const Collection = require('./collection');
15
- const mergeOptionsAndWriteConcern = require('./utils').mergeOptionsAndWriteConcern;
15
+ const conditionallyMergeWriteConcern = require('./utils').conditionallyMergeWriteConcern;
16
16
  const executeLegacyOperation = require('./utils').executeLegacyOperation;
17
17
  const ChangeStream = require('./change_stream');
18
18
  const deprecate = require('util').deprecate;
@@ -382,7 +382,7 @@ Db.prototype.admin = function() {
382
382
  * @param {AggregationCursor} cursor The cursor if the aggregation command was executed successfully.
383
383
  */
384
384
 
385
- const collectionKeys = [
385
+ const COLLECTION_OPTION_KEYS = [
386
386
  'pkFactory',
387
387
  'readPreference',
388
388
  'serializeFunctions',
@@ -433,8 +433,14 @@ Db.prototype.collection = function(name, options, callback) {
433
433
  options.ignoreUndefined = this.s.options.ignoreUndefined;
434
434
  }
435
435
 
436
+ for (const collectionOptionKey of COLLECTION_OPTION_KEYS) {
437
+ if (!(collectionOptionKey in options) && this.s.options[collectionOptionKey] !== undefined) {
438
+ options[collectionOptionKey] = this.s.options[collectionOptionKey];
439
+ }
440
+ }
441
+
436
442
  // Merge in all needed options and ensure correct writeConcern merging from db level
437
- options = mergeOptionsAndWriteConcern(options, this.s.options, collectionKeys, true);
443
+ options = conditionallyMergeWriteConcern(options, this.s.options);
438
444
 
439
445
  // Execute
440
446
  if (options == null || !options.strict) {
@@ -457,10 +457,6 @@ MongoClient.prototype.startSession = function(options) {
457
457
  throw new MongoError('Must connect to a server before calling this method');
458
458
  }
459
459
 
460
- if (!this.topology.hasSessionSupport()) {
461
- throw new MongoError('Current topology does not support sessions');
462
- }
463
-
464
460
  return this.topology.startSession(options, this.s.options);
465
461
  };
466
462
 
@@ -6,6 +6,7 @@ const defineAspects = require('./operation').defineAspects;
6
6
  const crypto = require('crypto');
7
7
  const handleCallback = require('../utils').handleCallback;
8
8
  const toError = require('../utils').toError;
9
+ const emitWarning = require('../utils').emitWarning;
9
10
 
10
11
  class AddUserOperation extends CommandOperation {
11
12
  constructor(db, username, password, options) {
@@ -29,7 +30,7 @@ class AddUserOperation extends CommandOperation {
29
30
  // If not roles defined print deprecated message
30
31
  // TODO: handle deprecation properly
31
32
  if (roles.length === 0) {
32
- console.log('Creating a user without roles is deprecated in MongoDB >= 2.6');
33
+ emitWarning('Creating a user without roles is deprecated in MongoDB >= 2.6');
33
34
  }
34
35
 
35
36
  // Check the db name and add roles if needed
@@ -13,6 +13,7 @@ const ReplSet = require('../topologies/replset');
13
13
  const Server = require('../topologies/server');
14
14
  const ServerSessionPool = require('../core').Sessions.ServerSessionPool;
15
15
  const emitDeprecationWarning = require('../utils').emitDeprecationWarning;
16
+ const emitWarningOnce = require('../utils').emitWarningOnce;
16
17
  const fs = require('fs');
17
18
  const WriteConcern = require('../write_concern');
18
19
  const BSON = require('../core/connection/utils').retrieveBSON();
@@ -182,12 +183,12 @@ function validOptions(options) {
182
183
  if (options.validateOptions) {
183
184
  return new MongoError(`option ${name} is not supported`);
184
185
  } else {
185
- console.warn(`the options [${name}] is not supported`);
186
+ emitWarningOnce(`the options [${name}] is not supported`);
186
187
  }
187
188
  }
188
189
 
189
190
  if (legacyOptionNames.indexOf(name) !== -1) {
190
- console.warn(
191
+ emitWarningOnce(
191
192
  `the server/replset/mongos/db options are deprecated, ` +
192
193
  `all their options are supported at the top level of the options object [${validOptionNames}]`
193
194
  );
@@ -257,9 +258,6 @@ function resolveTLSOptions(options) {
257
258
  });
258
259
  }
259
260
 
260
- const emitDeprecationForNonUnifiedTopology = deprecate(() => {},
261
- 'current Server Discovery and Monitoring engine is deprecated, and will be removed in a future version. ' + 'To use the new Server Discover and Monitoring engine, pass option { useUnifiedTopology: true } to the MongoClient constructor.');
262
-
263
261
  function connect(mongoClient, url, options, callback) {
264
262
  options = Object.assign({}, options);
265
263
 
@@ -335,7 +333,9 @@ function connect(mongoClient, url, options, callback) {
335
333
  return createTopology(mongoClient, 'unified', _finalOptions, connectCallback);
336
334
  }
337
335
 
338
- emitDeprecationForNonUnifiedTopology();
336
+ emitWarningOnce(
337
+ 'Current Server Discovery and Monitoring engine is deprecated, and will be removed in a future version. To use the new Server Discover and Monitoring engine, pass option { useUnifiedTopology: true } to the MongoClient constructor.'
338
+ );
339
339
 
340
340
  // Do we have a replicaset then skip discovery and go straight to connectivity
341
341
  if (_finalOptions.replicaSet || _finalOptions.rs_name) {
@@ -1,5 +1,6 @@
1
1
  'use strict';
2
2
 
3
+ const maybePromise = require('../utils').maybePromise;
3
4
  const MongoError = require('../core/error').MongoError;
4
5
  const Aspect = require('./operation').Aspect;
5
6
  const OperationBase = require('./operation').OperationBase;
@@ -21,7 +22,7 @@ const isUnifiedTopology = require('../core/utils').isUnifiedTopology;
21
22
  * @param {Operation} operation The operation to execute
22
23
  * @param {function} callback The command result callback
23
24
  */
24
- function executeOperation(topology, operation, callback) {
25
+ function executeOperation(topology, operation, cb) {
25
26
  if (topology == null) {
26
27
  throw new TypeError('This method requires a valid topology instance');
27
28
  }
@@ -30,64 +31,57 @@ function executeOperation(topology, operation, callback) {
30
31
  throw new TypeError('This method requires a valid operation instance');
31
32
  }
32
33
 
33
- if (isUnifiedTopology(topology) && topology.shouldCheckForSessionSupport()) {
34
- return selectServerForSessionSupport(topology, operation, callback);
35
- }
36
-
37
- const Promise = topology.s.promiseLibrary;
38
-
39
- // The driver sessions spec mandates that we implicitly create sessions for operations
40
- // that are not explicitly provided with a session.
41
- let session, owner;
42
- if (topology.hasSessionSupport()) {
43
- if (operation.session == null) {
44
- owner = Symbol();
45
- session = topology.startSession({ owner });
46
- operation.session = session;
47
- } else if (operation.session.hasEnded) {
48
- throw new MongoError('Use of expired sessions is not permitted');
34
+ return maybePromise(topology, cb, callback => {
35
+ if (isUnifiedTopology(topology) && topology.shouldCheckForSessionSupport()) {
36
+ // Recursive call to executeOperation after a server selection
37
+ return selectServerForSessionSupport(topology, operation, callback);
49
38
  }
50
- }
51
-
52
- let result;
53
- if (typeof callback !== 'function') {
54
- result = new Promise((resolve, reject) => {
55
- callback = (err, res) => {
56
- if (err) return reject(err);
57
- resolve(res);
58
- };
59
- });
60
- }
61
39
 
62
- function executeCallback(err, result) {
63
- if (session && session.owner === owner) {
64
- session.endSession();
65
- if (operation.session === session) {
66
- operation.clearSession();
40
+ // The driver sessions spec mandates that we implicitly create sessions for operations
41
+ // that are not explicitly provided with a session.
42
+ let session, owner;
43
+ if (topology.hasSessionSupport()) {
44
+ if (operation.session == null) {
45
+ owner = Symbol();
46
+ session = topology.startSession({ owner });
47
+ operation.session = session;
48
+ } else if (operation.session.hasEnded) {
49
+ return callback(new MongoError('Use of expired sessions is not permitted'));
67
50
  }
51
+ } else if (operation.session) {
52
+ // If the user passed an explicit session and we are still, after server selection,
53
+ // trying to run against a topology that doesn't support sessions we error out.
54
+ return callback(new MongoError('Current topology does not support sessions'));
68
55
  }
69
56
 
70
- callback(err, result);
71
- }
72
-
73
- try {
74
- if (operation.hasAspect(Aspect.EXECUTE_WITH_SELECTION)) {
75
- executeWithServerSelection(topology, operation, executeCallback);
76
- } else {
77
- operation.execute(executeCallback);
78
- }
79
- } catch (e) {
80
- if (session && session.owner === owner) {
81
- session.endSession();
82
- if (operation.session === session) {
83
- operation.clearSession();
57
+ function executeCallback(err, result) {
58
+ if (session && session.owner === owner) {
59
+ session.endSession();
60
+ if (operation.session === session) {
61
+ operation.clearSession();
62
+ }
84
63
  }
64
+
65
+ callback(err, result);
85
66
  }
86
67
 
87
- throw e;
88
- }
68
+ try {
69
+ if (operation.hasAspect(Aspect.EXECUTE_WITH_SELECTION)) {
70
+ executeWithServerSelection(topology, operation, executeCallback);
71
+ } else {
72
+ operation.execute(executeCallback);
73
+ }
74
+ } catch (error) {
75
+ if (session && session.owner === owner) {
76
+ session.endSession();
77
+ if (operation.session === session) {
78
+ operation.clearSession();
79
+ }
80
+ }
89
81
 
90
- return result;
82
+ callback(error);
83
+ }
84
+ });
91
85
  }
92
86
 
93
87
  function supportsRetryableReads(server) {
@@ -139,7 +133,6 @@ function executeWithServerSelection(topology, operation, callback) {
139
133
  callback(err, null);
140
134
  return;
141
135
  }
142
-
143
136
  const shouldRetryReads =
144
137
  topology.s.options.retryReads !== false &&
145
138
  operation.session &&
@@ -156,31 +149,16 @@ function executeWithServerSelection(topology, operation, callback) {
156
149
  });
157
150
  }
158
151
 
159
- // TODO: This is only supported for unified topology, it should go away once
160
- // we remove support for legacy topology types.
152
+ // The Unified Topology runs serverSelection before executing every operation
153
+ // Session support is determined by the result of a monitoring check triggered by this selection
161
154
  function selectServerForSessionSupport(topology, operation, callback) {
162
- const Promise = topology.s.promiseLibrary;
163
-
164
- let result;
165
- if (typeof callback !== 'function') {
166
- result = new Promise((resolve, reject) => {
167
- callback = (err, result) => {
168
- if (err) return reject(err);
169
- resolve(result);
170
- };
171
- });
172
- }
173
-
174
155
  topology.selectServer(ReadPreference.primaryPreferred, err => {
175
156
  if (err) {
176
- callback(err);
177
- return;
157
+ return callback(err);
178
158
  }
179
159
 
180
160
  executeOperation(topology, operation, callback);
181
161
  });
182
-
183
- return result;
184
162
  }
185
163
 
186
164
  module.exports = executeOperation;
@@ -1,7 +1,7 @@
1
1
  'use strict';
2
2
 
3
3
  const Explain = require('../explain').Explain;
4
- const MongoError = require('../core').MongoError;
4
+ const MongoError = require('../core/error').MongoError;
5
5
 
6
6
  const Aspect = {
7
7
  READ_OPERATION: Symbol('READ_OPERATION'),
package/lib/utils.js CHANGED
@@ -287,39 +287,38 @@ var filterOptions = function(options, names) {
287
287
  };
288
288
 
289
289
  // Write concern keys
290
- var writeConcernKeys = ['w', 'j', 'wtimeout', 'fsync'];
290
+ const WRITE_CONCERN_KEYS = ['w', 'j', 'wtimeout', 'fsync', 'writeConcern'];
291
291
 
292
- // Merge the write concern options
293
- var mergeOptionsAndWriteConcern = function(targetOptions, sourceOptions, keys, mergeWriteConcern) {
294
- // Mix in any allowed options
295
- for (var i = 0; i < keys.length; i++) {
296
- if (!targetOptions[keys[i]] && sourceOptions[keys[i]] !== undefined) {
297
- targetOptions[keys[i]] = sourceOptions[keys[i]];
298
- }
299
- }
300
-
301
- // No merging of write concern
302
- if (!mergeWriteConcern) return targetOptions;
303
-
304
- // Found no write Concern options
305
- var found = false;
306
- for (i = 0; i < writeConcernKeys.length; i++) {
307
- if (targetOptions[writeConcernKeys[i]]) {
292
+ /**
293
+ * If there is no WriteConcern related options defined on target then inherit from source.
294
+ * Otherwise, do not inherit **any** options from source.
295
+ * @internal
296
+ * @param {object} target - options object conditionally receiving the writeConcern options
297
+ * @param {object} source - options object containing the potentially inherited writeConcern options
298
+ */
299
+ function conditionallyMergeWriteConcern(target, source) {
300
+ let found = false;
301
+ for (const wcKey of WRITE_CONCERN_KEYS) {
302
+ if (wcKey in target) {
303
+ // Found a writeConcern option
308
304
  found = true;
309
305
  break;
310
306
  }
311
307
  }
312
308
 
313
309
  if (!found) {
314
- for (i = 0; i < writeConcernKeys.length; i++) {
315
- if (sourceOptions[writeConcernKeys[i]]) {
316
- targetOptions[writeConcernKeys[i]] = sourceOptions[writeConcernKeys[i]];
310
+ for (const wcKey of WRITE_CONCERN_KEYS) {
311
+ if (source[wcKey]) {
312
+ if (!('writeConcern' in target)) {
313
+ target.writeConcern = {};
314
+ }
315
+ target.writeConcern[wcKey] = source[wcKey];
317
316
  }
318
317
  }
319
318
  }
320
319
 
321
- return targetOptions;
322
- };
320
+ return target;
321
+ }
323
322
 
324
323
  /**
325
324
  * Executes the given operation with provided arguments.
@@ -534,7 +533,12 @@ function decorateWithExplain(command, explain) {
534
533
  return { explain: command, verbosity: explain.verbosity };
535
534
  }
536
535
 
537
- const emitProcessWarning = msg => process.emitWarning(msg, 'DeprecationWarning');
536
+ const nodejsMajorVersion = +process.version.split('.')[0].substring(1);
537
+ const emitProcessWarning = msg =>
538
+ nodejsMajorVersion <= 6
539
+ ? process.emitWarning(msg, 'DeprecationWarning', MONGODB_WARNING_CODE)
540
+ : process.emitWarning(msg, { type: 'DeprecationWarning', code: MONGODB_WARNING_CODE });
541
+ // eslint-disable-next-line no-console
538
542
  const emitConsoleWarning = msg => console.error(msg);
539
543
  const emitDeprecationWarning = process.emitWarning ? emitProcessWarning : emitConsoleWarning;
540
544
 
@@ -816,6 +820,50 @@ function hasAtomicOperators(doc) {
816
820
  );
817
821
  }
818
822
 
823
+ /**
824
+ * When the driver used emitWarning the code will be equal to this.
825
+ * @public
826
+ *
827
+ * @example
828
+ * ```js
829
+ * process.on('warning', (warning) => {
830
+ * if (warning.code === MONGODB_WARNING_CODE) console.error('Ah an important warning! :)')
831
+ * })
832
+ * ```
833
+ */
834
+ const MONGODB_WARNING_CODE = 'MONGODB DRIVER';
835
+
836
+ /**
837
+ * @internal
838
+ * @param {string} message - message to warn about
839
+ */
840
+ function emitWarning(message) {
841
+ if (process.emitWarning) {
842
+ return nodejsMajorVersion <= 6
843
+ ? process.emitWarning(message, undefined, MONGODB_WARNING_CODE)
844
+ : process.emitWarning(message, { code: MONGODB_WARNING_CODE });
845
+ } else {
846
+ // Approximate the style of print out on node versions pre 8.x
847
+ // eslint-disable-next-line no-console
848
+ return console.error(`[${MONGODB_WARNING_CODE}] Warning:`, message);
849
+ }
850
+ }
851
+
852
+ const emittedWarnings = new Set();
853
+ /**
854
+ * Will emit a warning once for the duration of the application.
855
+ * Uses the message to identify if it has already been emitted
856
+ * so using string interpolation can cause multiple emits
857
+ * @internal
858
+ * @param {string} message - message to warn about
859
+ */
860
+ function emitWarningOnce(message) {
861
+ if (!emittedWarnings.has(message)) {
862
+ emittedWarnings.add(message);
863
+ return emitWarning(message);
864
+ }
865
+ }
866
+
819
867
  module.exports = {
820
868
  filterOptions,
821
869
  mergeOptions,
@@ -832,7 +880,7 @@ module.exports = {
832
880
  isObject,
833
881
  debugOptions,
834
882
  MAX_JS_INT: Number.MAX_SAFE_INTEGER + 1,
835
- mergeOptionsAndWriteConcern,
883
+ conditionallyMergeWriteConcern,
836
884
  executeLegacyOperation,
837
885
  applyRetryableWrites,
838
886
  applyWriteConcern,
@@ -849,5 +897,8 @@ module.exports = {
849
897
  now,
850
898
  calculateDurationInMs,
851
899
  makeInterruptableAsyncInterval,
852
- hasAtomicOperators
900
+ hasAtomicOperators,
901
+ MONGODB_WARNING_CODE,
902
+ emitWarning,
903
+ emitWarningOnce
853
904
  };
@@ -1,6 +1,7 @@
1
1
  'use strict';
2
2
 
3
3
  const kWriteConcernKeys = new Set(['w', 'wtimeout', 'j', 'journal', 'fsync']);
4
+ let utils;
4
5
 
5
6
  /**
6
7
  * The **WriteConcern** class is a class that represents a MongoDB WriteConcern.
@@ -75,7 +76,10 @@ class WriteConcern {
75
76
  );
76
77
  }
77
78
 
78
- console.warn(
79
+ // this is down here to prevent circular dependency
80
+ if (!utils) utils = require('./utils');
81
+
82
+ utils.emitWarningOnce(
79
83
  `Top-level use of w, wtimeout, j, and fsync is deprecated. Use writeConcern instead.`
80
84
  );
81
85
  return new WriteConcern(
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "mongodb",
3
- "version": "3.6.4",
3
+ "version": "3.6.5",
4
4
  "description": "The official MongoDB driver for Node.js",
5
5
  "main": "index.js",
6
6
  "files": [