mongodb 3.6.9 → 3.7.0

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.
Files changed (53) hide show
  1. package/README.md +4 -4
  2. package/index.js +4 -0
  3. package/lib/bulk/common.js +6 -3
  4. package/lib/cmap/connection.js +35 -7
  5. package/lib/cmap/connection_pool.js +1 -0
  6. package/lib/collection.js +10 -1
  7. package/lib/command_utils.js +124 -0
  8. package/lib/core/auth/mongo_credentials.js +4 -1
  9. package/lib/core/auth/mongodb_aws.js +17 -15
  10. package/lib/core/auth/scram.js +1 -0
  11. package/lib/core/connection/apm.js +10 -123
  12. package/lib/core/connection/commands.js +11 -2
  13. package/lib/core/connection/connect.js +10 -1
  14. package/lib/core/connection/connection.js +7 -3
  15. package/lib/core/connection/msg.js +11 -2
  16. package/lib/core/connection/pool.js +7 -2
  17. package/lib/core/connection/utils.js +35 -2
  18. package/lib/core/cursor.js +7 -0
  19. package/lib/core/index.js +10 -0
  20. package/lib/core/sdam/monitor.js +9 -2
  21. package/lib/core/sdam/server.js +10 -1
  22. package/lib/core/sdam/topology.js +34 -16
  23. package/lib/core/sessions.js +11 -8
  24. package/lib/core/topologies/mongos.js +1 -0
  25. package/lib/core/topologies/replset.js +1 -0
  26. package/lib/core/topologies/server.js +7 -2
  27. package/lib/core/transactions.js +5 -1
  28. package/lib/core/uri_parser.js +5 -0
  29. package/lib/core/utils.js +1 -0
  30. package/lib/core/wireprotocol/command.js +24 -0
  31. package/lib/core/wireprotocol/constants.js +2 -2
  32. package/lib/core/wireprotocol/kill_cursors.js +7 -2
  33. package/lib/core/wireprotocol/query.js +9 -5
  34. package/lib/core/wireprotocol/shared.js +1 -0
  35. package/lib/cursor.js +16 -2
  36. package/lib/db.js +83 -72
  37. package/lib/encrypter.js +8 -3
  38. package/lib/explain.js +5 -12
  39. package/lib/gridfs-stream/index.js +39 -24
  40. package/lib/gridfs-stream/upload.js +46 -38
  41. package/lib/mongo_client.js +31 -5
  42. package/lib/operations/command.js +4 -1
  43. package/lib/operations/command_v2.js +7 -1
  44. package/lib/operations/connect.js +2 -0
  45. package/lib/operations/db_ops.js +6 -2
  46. package/lib/operations/estimated_document_count.js +46 -18
  47. package/lib/topologies/mongos.js +1 -0
  48. package/lib/topologies/native_topology.js +2 -0
  49. package/lib/topologies/replset.js +1 -0
  50. package/lib/topologies/server.js +1 -0
  51. package/lib/utils.js +87 -1
  52. package/package.json +7 -3
  53. package/HISTORY.md +0 -2889
package/README.md CHANGED
@@ -15,10 +15,10 @@ Check out our [beta version 4.0 here](https://github.com/mongodb/node-mongodb-na
15
15
 
16
16
  | what | where |
17
17
  | ------------- | ---------------------------------------------------- |
18
- | documentation | http://mongodb.github.io/node-mongodb-native |
19
- | api-doc | http://mongodb.github.io/node-mongodb-native/3.6/api |
18
+ | documentation | https://mongodb.github.io/node-mongodb-native |
19
+ | api-doc | https://mongodb.github.io/node-mongodb-native/3.6/api |
20
20
  | source | https://github.com/mongodb/node-mongodb-native |
21
- | mongodb | http://www.mongodb.org |
21
+ | mongodb | https://www.mongodb.org |
22
22
 
23
23
  ### Bugs / Feature Requests
24
24
 
@@ -481,7 +481,7 @@ For more detailed information, see the [tutorials](docs/reference/content/tutori
481
481
 
482
482
  ## Next Steps
483
483
 
484
- - [MongoDB Documentation](http://mongodb.org)
484
+ - [MongoDB Documentation](https://mongodb.org)
485
485
  - [Read about Schemas](http://learnmongodbthehardway.com)
486
486
  - [Star us on GitHub](https://github.com/mongodb/node-mongodb-native)
487
487
 
package/index.js CHANGED
@@ -17,6 +17,9 @@ connect.MongoWriteConcernError = core.MongoWriteConcernError;
17
17
  connect.MongoBulkWriteError = require('./lib/bulk/common').BulkWriteError;
18
18
  connect.BulkWriteError = connect.MongoBulkWriteError;
19
19
 
20
+ // Expose server versions
21
+ connect.ServerApiVersion = core.ServerApiVersion;
22
+
20
23
  // Actual driver classes exported
21
24
  connect.Admin = require('./lib/admin');
22
25
  connect.MongoClient = require('./lib/mongo_client');
@@ -47,6 +50,7 @@ connect.Int32 = core.BSON.Int32;
47
50
  connect.Long = core.BSON.Long;
48
51
  connect.MinKey = core.BSON.MinKey;
49
52
  connect.MaxKey = core.BSON.MaxKey;
53
+ /** @deprecated Please use `ObjectId` */
50
54
  connect.ObjectID = core.BSON.ObjectID;
51
55
  connect.ObjectId = core.BSON.ObjectID;
52
56
  connect.Symbol = core.BSON.Symbol;
@@ -1176,8 +1176,11 @@ class BulkOperationBase {
1176
1176
  * @param {function} callback
1177
1177
  */
1178
1178
  bulkExecute(_writeConcern, options, callback) {
1179
- if (typeof options === 'function') (callback = options), (options = {});
1180
- options = options || {};
1179
+ if (typeof options === 'function') {
1180
+ callback = options;
1181
+ }
1182
+
1183
+ const finalOptions = Object.assign({}, this.s.options, options);
1181
1184
 
1182
1185
  if (typeof _writeConcern === 'function') {
1183
1186
  callback = _writeConcern;
@@ -1203,7 +1206,7 @@ class BulkOperationBase {
1203
1206
  const emptyBatchError = toError('Invalid Operation, no operations specified');
1204
1207
  return this._handleEarlyError(emptyBatchError, callback);
1205
1208
  }
1206
- return { options, callback };
1209
+ return { options: finalOptions, callback };
1207
1210
  }
1208
1211
 
1209
1212
  /**
@@ -37,6 +37,8 @@ class Connection extends EventEmitter {
37
37
  this.port = options.port || 27017;
38
38
  this.monitorCommands =
39
39
  typeof options.monitorCommands === 'boolean' ? options.monitorCommands : false;
40
+ this.serverApi = options.serverApi;
41
+
40
42
  this.closed = false;
41
43
  this.destroyed = false;
42
44
 
@@ -170,33 +172,58 @@ class Connection extends EventEmitter {
170
172
  });
171
173
  }
172
174
 
175
+ applyApiVersion(options) {
176
+ if (this.serverApi) {
177
+ options.serverApi = this.serverApi;
178
+ }
179
+ return options;
180
+ }
181
+
173
182
  // Wire protocol methods
174
183
  command(ns, cmd, options, callback) {
175
- wp.command(makeServerTrampoline(this), ns, cmd, options, callback);
184
+ if (typeof options === 'function') {
185
+ callback = options;
186
+ options = {};
187
+ }
188
+ wp.command(makeServerTrampoline(this), ns, cmd, this.applyApiVersion(options), callback);
176
189
  }
177
190
 
178
191
  query(ns, cmd, cursorState, options, callback) {
179
- wp.query(makeServerTrampoline(this), ns, cmd, cursorState, options, callback);
192
+ wp.query(
193
+ makeServerTrampoline(this),
194
+ ns,
195
+ cmd,
196
+ cursorState,
197
+ this.applyApiVersion(options),
198
+ callback
199
+ );
180
200
  }
181
201
 
182
202
  getMore(ns, cursorState, batchSize, options, callback) {
183
- wp.getMore(makeServerTrampoline(this), ns, cursorState, batchSize, options, callback);
203
+ wp.getMore(
204
+ makeServerTrampoline(this),
205
+ ns,
206
+ cursorState,
207
+ batchSize,
208
+ this.applyApiVersion(options),
209
+ callback
210
+ );
184
211
  }
185
212
 
186
213
  killCursors(ns, cursorState, callback) {
187
- wp.killCursors(makeServerTrampoline(this), ns, cursorState, callback);
214
+ wp.killCursors(makeServerTrampoline(this), ns, cursorState, this.applyApiVersion({}), callback);
188
215
  }
189
216
 
190
217
  insert(ns, ops, options, callback) {
191
- wp.insert(makeServerTrampoline(this), ns, ops, options, callback);
218
+ wp.insert(makeServerTrampoline(this), ns, ops, this.applyApiVersion(options), callback);
192
219
  }
193
220
 
194
221
  update(ns, ops, options, callback) {
195
- wp.update(makeServerTrampoline(this), ns, ops, options, callback);
222
+ wp.update(makeServerTrampoline(this), ns, ops, this.applyApiVersion(options), callback);
196
223
  }
197
224
 
198
225
  remove(ns, ops, options, callback) {
199
- wp.remove(makeServerTrampoline(this), ns, ops, options, callback);
226
+ wp.remove(makeServerTrampoline(this), ns, ops, this.applyApiVersion(options), callback);
200
227
  }
201
228
  }
202
229
 
@@ -317,6 +344,7 @@ function write(command, options, callback) {
317
344
  promoteLongs: typeof options.promoteLongs === 'boolean' ? options.promoteLongs : true,
318
345
  promoteValues: typeof options.promoteValues === 'boolean' ? options.promoteValues : true,
319
346
  promoteBuffers: typeof options.promoteBuffers === 'boolean' ? options.promoteBuffers : false,
347
+ bsonRegExp: typeof options.bsonRegExp === 'boolean' ? options.bsonRegExp : false,
320
348
  raw: typeof options.raw === 'boolean' ? options.raw : false
321
349
  };
322
350
 
@@ -41,6 +41,7 @@ const VALID_POOL_OPTIONS = new Set([
41
41
  'ssl',
42
42
  'bson',
43
43
  'connectionType',
44
+ 'serverApi',
44
45
  'monitorCommands',
45
46
  'socketTimeout',
46
47
  'credentials',
package/lib/collection.js CHANGED
@@ -120,6 +120,8 @@ function Collection(db, topology, dbName, name, pkFactory, options) {
120
120
  options == null || options.promoteBuffers == null
121
121
  ? db.s.options.promoteBuffers
122
122
  : options.promoteBuffers;
123
+ const bsonRegExp =
124
+ options == null || options.bsonRegExp == null ? db.s.options.bsonRegExp : options.bsonRegExp;
123
125
  const collectionHint = null;
124
126
 
125
127
  const namespace = new MongoDBNamespace(dbName, name);
@@ -156,6 +158,8 @@ function Collection(db, topology, dbName, name, pkFactory, options) {
156
158
  promoteValues: promoteValues,
157
159
  // promoteBuffers
158
160
  promoteBuffers: promoteBuffers,
161
+ // bsonRegExp
162
+ bsonRegExp: bsonRegExp,
159
163
  // internalHint
160
164
  internalHint: internalHint,
161
165
  // collectionHint
@@ -303,6 +307,7 @@ const DEPRECATED_FIND_OPTIONS = ['maxScan', 'fields', 'snapshot', 'oplogReplay']
303
307
  * @param {boolean} [options.promoteLongs=true] Promotes Long values to number if they fit inside the 53 bits resolution.
304
308
  * @param {boolean} [options.promoteValues=true] Promotes BSON values to native types where possible, set to false to only receive wrapper types.
305
309
  * @param {boolean} [options.promoteBuffers=false] Promotes Binary BSON values to native Node Buffers.
310
+ * @param {boolean} [options.bsonRegExp=false] By default, regex returned from MDB will be native to the language. Setting to true will ensure that a BSON.BSONRegExp object is returned.
306
311
  * @param {(ReadPreference|string)} [options.readPreference] The preferred read preference (ReadPreference.PRIMARY, ReadPreference.PRIMARY_PREFERRED, ReadPreference.SECONDARY, ReadPreference.SECONDARY_PREFERRED, ReadPreference.NEAREST).
307
312
  * @param {boolean} [options.partial=false] Specify if the cursor should return partial results when querying against a sharded system
308
313
  * @param {number} [options.maxTimeMS] Number of milliseconds to wait before aborting the query.
@@ -451,6 +456,8 @@ Collection.prototype.find = deprecateOptions(
451
456
  newOptions.promoteValues = this.s.promoteValues;
452
457
  if (newOptions.promoteBuffers == null && typeof this.s.promoteBuffers === 'boolean')
453
458
  newOptions.promoteBuffers = this.s.promoteBuffers;
459
+ if (newOptions.bsonRegExp == null && typeof this.s.bsonRegExp === 'boolean')
460
+ newOptions.bsonRegExp = this.s.bsonRegExp;
454
461
 
455
462
  // Sort options
456
463
  if (findCommand.sort) {
@@ -1075,6 +1082,7 @@ Collection.prototype.save = deprecate(function(doc, options, callback) {
1075
1082
  * @param {boolean} [options.promoteLongs=true] Promotes Long values to number if they fit inside the 53 bits resolution.
1076
1083
  * @param {boolean} [options.promoteValues=true] Promotes BSON values to native types where possible, set to false to only receive wrapper types.
1077
1084
  * @param {boolean} [options.promoteBuffers=false] Promotes Binary BSON values to native Node Buffers.
1085
+ * @param {boolean} [options.bsonRegExp=false] By default, regex returned from MDB will be native to the language. Setting to true will ensure that a BSON.BSONRegExp object is returned.
1078
1086
  * @param {(ReadPreference|string)} [options.readPreference] The preferred read preference (ReadPreference.PRIMARY, ReadPreference.PRIMARY_PREFERRED, ReadPreference.SECONDARY, ReadPreference.SECONDARY_PREFERRED, ReadPreference.NEAREST).
1079
1087
  * @param {boolean} [options.partial=false] Specify if the cursor should return partial results when querying against a sharded system
1080
1088
  * @param {number} [options.maxTimeMS] Number of milliseconds to wait before aborting the query.
@@ -1524,7 +1532,7 @@ Collection.prototype.count = deprecate(function(query, options, callback) {
1524
1532
 
1525
1533
  return executeOperation(
1526
1534
  this.s.topology,
1527
- new EstimatedDocumentCountOperation(this, query, options),
1535
+ new EstimatedDocumentCountOperation(this, Object.assign({ query }, options)),
1528
1536
  callback
1529
1537
  );
1530
1538
  }, 'collection.count is deprecated, and will be removed in a future version.' +
@@ -1899,6 +1907,7 @@ Collection.prototype.findAndRemove = deprecate(function(query, sort, options, ca
1899
1907
  * @param {boolean} [options.promoteLongs=true] Promotes Long values to number if they fit inside the 53 bits resolution.
1900
1908
  * @param {boolean} [options.promoteValues=true] Promotes BSON values to native types where possible, set to false to only receive wrapper types.
1901
1909
  * @param {boolean} [options.promoteBuffers=false] Promotes Binary BSON values to native Node Buffers.
1910
+ * @param {boolean} [options.bsonRegExp=false] By default, regex returned from MDB will be native to the language. Setting to true will ensure that a BSON.BSONRegExp object is returned.
1902
1911
  * @param {object} [options.collation] Specify collation settings for operation. See {@link https://docs.mongodb.com/manual/reference/command/aggregate|aggregation documentation}.
1903
1912
  * @param {string} [options.comment] Add a comment to an aggregation command
1904
1913
  * @param {string|object} [options.hint] Add an index selection hint to an aggregation command
@@ -0,0 +1,124 @@
1
+ 'use strict';
2
+ const Msg = require('./core/connection/msg').Msg;
3
+ const KillCursor = require('./core/connection/commands').KillCursor;
4
+ const GetMore = require('./core/connection/commands').GetMore;
5
+ const deepCopy = require('./utils').deepCopy;
6
+
7
+ /** Commands that we want to redact because of the sensitive nature of their contents */
8
+ const SENSITIVE_COMMANDS = new Set([
9
+ 'authenticate',
10
+ 'saslStart',
11
+ 'saslContinue',
12
+ 'getnonce',
13
+ 'createUser',
14
+ 'updateUser',
15
+ 'copydbgetnonce',
16
+ 'copydbsaslstart',
17
+ 'copydb'
18
+ ]);
19
+
20
+ const HELLO_COMMANDS = new Set(['hello', 'ismaster', 'isMaster']);
21
+
22
+ const LEGACY_FIND_QUERY_MAP = {
23
+ $query: 'filter',
24
+ $orderby: 'sort',
25
+ $hint: 'hint',
26
+ $comment: 'comment',
27
+ $maxScan: 'maxScan',
28
+ $max: 'max',
29
+ $min: 'min',
30
+ $returnKey: 'returnKey',
31
+ $showDiskLoc: 'showRecordId',
32
+ $maxTimeMS: 'maxTimeMS',
33
+ $snapshot: 'snapshot'
34
+ };
35
+
36
+ const LEGACY_FIND_OPTIONS_MAP = {
37
+ numberToSkip: 'skip',
38
+ numberToReturn: 'batchSize',
39
+ returnFieldsSelector: 'projection'
40
+ };
41
+
42
+ const OP_QUERY_KEYS = [
43
+ 'tailable',
44
+ 'oplogReplay',
45
+ 'noCursorTimeout',
46
+ 'awaitData',
47
+ 'partial',
48
+ 'exhaust'
49
+ ];
50
+
51
+ const collectionName = command => command.ns.split('.')[1];
52
+
53
+ const shouldRedactCommand = (commandName, cmd) =>
54
+ SENSITIVE_COMMANDS.has(commandName) ||
55
+ (HELLO_COMMANDS.has(commandName) && !!cmd.speculativeAuthenticate);
56
+
57
+ /**
58
+ * Extract the actual command from the query, possibly upconverting if it's a legacy
59
+ * format
60
+ *
61
+ * @param {Object} command the command
62
+ */
63
+ const extractCommand = command => {
64
+ let extractedCommand;
65
+ if (command instanceof GetMore) {
66
+ extractedCommand = {
67
+ getMore: deepCopy(command.cursorId),
68
+ collection: collectionName(command),
69
+ batchSize: command.numberToReturn
70
+ };
71
+ } else if (command instanceof KillCursor) {
72
+ extractedCommand = {
73
+ killCursors: collectionName(command),
74
+ cursors: deepCopy(command.cursorIds)
75
+ };
76
+ } else if (command instanceof Msg) {
77
+ extractedCommand = deepCopy(command.command);
78
+ } else if (command.query && command.query.$query) {
79
+ let result;
80
+ if (command.ns === 'admin.$cmd') {
81
+ // upconvert legacy command
82
+ result = Object.assign({}, command.query.$query);
83
+ } else {
84
+ // upconvert legacy find command
85
+ result = { find: collectionName(command) };
86
+ Object.keys(LEGACY_FIND_QUERY_MAP).forEach(key => {
87
+ if (typeof command.query[key] !== 'undefined')
88
+ result[LEGACY_FIND_QUERY_MAP[key]] = deepCopy(command.query[key]);
89
+ });
90
+ }
91
+
92
+ Object.keys(LEGACY_FIND_OPTIONS_MAP).forEach(key => {
93
+ if (typeof command[key] !== 'undefined')
94
+ result[LEGACY_FIND_OPTIONS_MAP[key]] = deepCopy(command[key]);
95
+ });
96
+
97
+ OP_QUERY_KEYS.forEach(key => {
98
+ if (command[key]) result[key] = command[key];
99
+ });
100
+
101
+ if (typeof command.pre32Limit !== 'undefined') {
102
+ result.limit = command.pre32Limit;
103
+ }
104
+
105
+ if (command.query.$explain) {
106
+ extractedCommand = { explain: result };
107
+ } else {
108
+ extractedCommand = result;
109
+ }
110
+ } else {
111
+ extractedCommand = deepCopy(command.query || command);
112
+ }
113
+
114
+ const commandName = Object.keys(extractedCommand)[0];
115
+ return {
116
+ cmd: extractedCommand,
117
+ name: commandName,
118
+ shouldRedact: shouldRedactCommand(commandName, extractedCommand)
119
+ };
120
+ };
121
+
122
+ module.exports = {
123
+ extractCommand
124
+ };
@@ -58,7 +58,10 @@ class MongoCredentials {
58
58
  this.password = process.env.AWS_SECRET_ACCESS_KEY;
59
59
  }
60
60
 
61
- if (!this.mechanismProperties.AWS_SESSION_TOKEN && process.env.AWS_SESSION_TOKEN) {
61
+ if (
62
+ this.mechanismProperties.AWS_SESSION_TOKEN == null &&
63
+ process.env.AWS_SESSION_TOKEN != null
64
+ ) {
62
65
  this.mechanismProperties.AWS_SESSION_TOKEN = process.env.AWS_SESSION_TOKEN;
63
66
  }
64
67
  }
@@ -9,6 +9,7 @@ const url = require('url');
9
9
 
10
10
  let aws4;
11
11
  try {
12
+ // Ensure you always wrap an optional require in the try block NODE-3199
12
13
  aws4 = require('aws4');
13
14
  } catch (e) {
14
15
  // don't do anything;
@@ -50,12 +51,21 @@ class MongoDBAWS extends AuthProvider {
50
51
  return;
51
52
  }
52
53
 
53
- const username = credentials.username;
54
- const password = credentials.password;
55
54
  const db = credentials.source;
56
- const token = credentials.mechanismProperties.AWS_SESSION_TOKEN;
57
55
  const bson = this.bson;
58
56
 
57
+ const accessKeyId = credentials.username;
58
+ const secretAccessKey = credentials.password;
59
+ const sessionToken = credentials.mechanismProperties.AWS_SESSION_TOKEN;
60
+
61
+ // If all three defined, include sessionToken, else include username and pass, else no credentials
62
+ const awsCredentials =
63
+ accessKeyId && secretAccessKey && sessionToken
64
+ ? { accessKeyId, secretAccessKey, sessionToken }
65
+ : accessKeyId && secretAccessKey
66
+ ? { accessKeyId, secretAccessKey }
67
+ : undefined;
68
+
59
69
  crypto.randomBytes(32, (err, nonce) => {
60
70
  if (err) {
61
71
  callback(err);
@@ -108,18 +118,14 @@ class MongoDBAWS extends AuthProvider {
108
118
  path: '/',
109
119
  body
110
120
  },
111
- {
112
- accessKeyId: username,
113
- secretAccessKey: password,
114
- token
115
- }
121
+ awsCredentials
116
122
  );
117
123
 
118
124
  const authorization = options.headers.Authorization;
119
125
  const date = options.headers['X-Amz-Date'];
120
126
  const payload = { a: authorization, d: date };
121
- if (token) {
122
- payload.t = token;
127
+ if (sessionToken) {
128
+ payload.t = sessionToken;
123
129
  }
124
130
 
125
131
  const saslContinue = {
@@ -163,6 +169,7 @@ function makeTempCredentials(credentials, callback) {
163
169
  if (process.env.AWS_CONTAINER_CREDENTIALS_RELATIVE_URI) {
164
170
  request(
165
171
  `${AWS_RELATIVE_URI}${process.env.AWS_CONTAINER_CREDENTIALS_RELATIVE_URI}`,
172
+ undefined,
166
173
  (err, res) => {
167
174
  if (err) return callback(err);
168
175
  done(res);
@@ -214,11 +221,6 @@ function deriveRegion(host) {
214
221
  }
215
222
 
216
223
  function request(uri, options, callback) {
217
- if (typeof options === 'function') {
218
- callback = options;
219
- options = {};
220
- }
221
-
222
224
  options = Object.assign(
223
225
  {
224
226
  method: 'GET',
@@ -11,6 +11,7 @@ const Binary = BSON.Binary;
11
11
 
12
12
  let saslprep;
13
13
  try {
14
+ // Ensure you always wrap an optional require in the try block NODE-3199
14
15
  saslprep = require('saslprep');
15
16
  } catch (e) {
16
17
  // don't do anything;
@@ -1,123 +1,16 @@
1
1
  'use strict';
2
- const Msg = require('../connection/msg').Msg;
3
2
  const KillCursor = require('../connection/commands').KillCursor;
4
3
  const GetMore = require('../connection/commands').GetMore;
5
4
  const calculateDurationInMs = require('../../utils').calculateDurationInMs;
6
-
7
- /** Commands that we want to redact because of the sensitive nature of their contents */
8
- const SENSITIVE_COMMANDS = new Set([
9
- 'authenticate',
10
- 'saslStart',
11
- 'saslContinue',
12
- 'getnonce',
13
- 'createUser',
14
- 'updateUser',
15
- 'copydbgetnonce',
16
- 'copydbsaslstart',
17
- 'copydb'
18
- ]);
5
+ const extractCommand = require('../../command_utils').extractCommand;
19
6
 
20
7
  // helper methods
21
- const extractCommandName = commandDoc => Object.keys(commandDoc)[0];
22
8
  const namespace = command => command.ns;
23
9
  const databaseName = command => command.ns.split('.')[0];
24
- const collectionName = command => command.ns.split('.')[1];
25
10
  const generateConnectionId = pool =>
26
11
  pool.options ? `${pool.options.host}:${pool.options.port}` : pool.address;
27
- const maybeRedact = (commandName, result) => (SENSITIVE_COMMANDS.has(commandName) ? {} : result);
28
12
  const isLegacyPool = pool => pool.s && pool.queue;
29
13
 
30
- const LEGACY_FIND_QUERY_MAP = {
31
- $query: 'filter',
32
- $orderby: 'sort',
33
- $hint: 'hint',
34
- $comment: 'comment',
35
- $maxScan: 'maxScan',
36
- $max: 'max',
37
- $min: 'min',
38
- $returnKey: 'returnKey',
39
- $showDiskLoc: 'showRecordId',
40
- $maxTimeMS: 'maxTimeMS',
41
- $snapshot: 'snapshot'
42
- };
43
-
44
- const LEGACY_FIND_OPTIONS_MAP = {
45
- numberToSkip: 'skip',
46
- numberToReturn: 'batchSize',
47
- returnFieldsSelector: 'projection'
48
- };
49
-
50
- const OP_QUERY_KEYS = [
51
- 'tailable',
52
- 'oplogReplay',
53
- 'noCursorTimeout',
54
- 'awaitData',
55
- 'partial',
56
- 'exhaust'
57
- ];
58
-
59
- /**
60
- * Extract the actual command from the query, possibly upconverting if it's a legacy
61
- * format
62
- *
63
- * @param {Object} command the command
64
- */
65
- const extractCommand = command => {
66
- if (command instanceof GetMore) {
67
- return {
68
- getMore: command.cursorId,
69
- collection: collectionName(command),
70
- batchSize: command.numberToReturn
71
- };
72
- }
73
-
74
- if (command instanceof KillCursor) {
75
- return {
76
- killCursors: collectionName(command),
77
- cursors: command.cursorIds
78
- };
79
- }
80
-
81
- if (command instanceof Msg) {
82
- return command.command;
83
- }
84
-
85
- if (command.query && command.query.$query) {
86
- let result;
87
- if (command.ns === 'admin.$cmd') {
88
- // upconvert legacy command
89
- result = Object.assign({}, command.query.$query);
90
- } else {
91
- // upconvert legacy find command
92
- result = { find: collectionName(command) };
93
- Object.keys(LEGACY_FIND_QUERY_MAP).forEach(key => {
94
- if (typeof command.query[key] !== 'undefined')
95
- result[LEGACY_FIND_QUERY_MAP[key]] = command.query[key];
96
- });
97
- }
98
-
99
- Object.keys(LEGACY_FIND_OPTIONS_MAP).forEach(key => {
100
- if (typeof command[key] !== 'undefined') result[LEGACY_FIND_OPTIONS_MAP[key]] = command[key];
101
- });
102
-
103
- OP_QUERY_KEYS.forEach(key => {
104
- if (command[key]) result[key] = command[key];
105
- });
106
-
107
- if (typeof command.pre32Limit !== 'undefined') {
108
- result.limit = command.pre32Limit;
109
- }
110
-
111
- if (command.query.$explain) {
112
- return { explain: result };
113
- }
114
-
115
- return result;
116
- }
117
-
118
- return command.query ? command.query : command;
119
- };
120
-
121
14
  const extractReply = (command, reply) => {
122
15
  if (command instanceof GetMore) {
123
16
  return {
@@ -177,21 +70,15 @@ class CommandStartedEvent {
177
70
  * @param {Object} command the command
178
71
  */
179
72
  constructor(pool, command) {
180
- const cmd = extractCommand(command);
181
- const commandName = extractCommandName(cmd);
73
+ const extractedCommand = extractCommand(command);
74
+ const commandName = extractedCommand.name;
182
75
  const connectionDetails = extractConnectionDetails(pool);
183
76
 
184
- // NOTE: remove in major revision, this is not spec behavior
185
- if (SENSITIVE_COMMANDS.has(commandName)) {
186
- this.commandObj = {};
187
- this.commandObj[commandName] = true;
188
- }
189
-
190
77
  Object.assign(this, connectionDetails, {
191
78
  requestId: command.requestId,
192
79
  databaseName: databaseName(command),
193
80
  commandName,
194
- command: cmd
81
+ command: extractedCommand.shouldRedact ? {} : extractedCommand.cmd
195
82
  });
196
83
  }
197
84
  }
@@ -207,15 +94,15 @@ class CommandSucceededEvent {
207
94
  * @param {Array} started a high resolution tuple timestamp of when the command was first sent, to calculate duration
208
95
  */
209
96
  constructor(pool, command, reply, started) {
210
- const cmd = extractCommand(command);
211
- const commandName = extractCommandName(cmd);
97
+ const extractedCommand = extractCommand(command);
98
+ const commandName = extractedCommand.name;
212
99
  const connectionDetails = extractConnectionDetails(pool);
213
100
 
214
101
  Object.assign(this, connectionDetails, {
215
102
  requestId: command.requestId,
216
103
  commandName,
217
104
  duration: calculateDurationInMs(started),
218
- reply: maybeRedact(commandName, extractReply(command, reply))
105
+ reply: extractedCommand.shouldRedact ? {} : extractReply(command, reply)
219
106
  });
220
107
  }
221
108
  }
@@ -231,15 +118,15 @@ class CommandFailedEvent {
231
118
  * @param {Array} started a high resolution tuple timestamp of when the command was first sent, to calculate duration
232
119
  */
233
120
  constructor(pool, command, error, started) {
234
- const cmd = extractCommand(command);
235
- const commandName = extractCommandName(cmd);
121
+ const extractedCommand = extractCommand(command);
122
+ const commandName = extractedCommand.name;
236
123
  const connectionDetails = extractConnectionDetails(pool);
237
124
 
238
125
  Object.assign(this, connectionDetails, {
239
126
  requestId: command.requestId,
240
127
  commandName,
241
128
  duration: calculateDurationInMs(started),
242
- failure: maybeRedact(commandName, error)
129
+ failure: extractedCommand.shouldRedact ? {} : error
243
130
  });
244
131
  }
245
132
  }
@@ -398,7 +398,12 @@ KillCursor.prototype.toBin = function() {
398
398
  };
399
399
 
400
400
  var Response = function(bson, message, msgHeader, msgBody, opts) {
401
- opts = opts || { promoteLongs: true, promoteValues: true, promoteBuffers: false };
401
+ opts = opts || {
402
+ promoteLongs: true,
403
+ promoteValues: true,
404
+ promoteBuffers: false,
405
+ bsonRegExp: false
406
+ };
402
407
  this.parsed = false;
403
408
  this.raw = message;
404
409
  this.data = msgBody;
@@ -429,6 +434,7 @@ var Response = function(bson, message, msgHeader, msgBody, opts) {
429
434
  this.promoteLongs = typeof opts.promoteLongs === 'boolean' ? opts.promoteLongs : true;
430
435
  this.promoteValues = typeof opts.promoteValues === 'boolean' ? opts.promoteValues : true;
431
436
  this.promoteBuffers = typeof opts.promoteBuffers === 'boolean' ? opts.promoteBuffers : false;
437
+ this.bsonRegExp = typeof opts.bsonRegExp === 'boolean' ? opts.bsonRegExp : false;
432
438
  };
433
439
 
434
440
  Response.prototype.isParsed = function() {
@@ -449,13 +455,16 @@ Response.prototype.parse = function(options) {
449
455
  typeof options.promoteValues === 'boolean' ? options.promoteValues : this.opts.promoteValues;
450
456
  var promoteBuffers =
451
457
  typeof options.promoteBuffers === 'boolean' ? options.promoteBuffers : this.opts.promoteBuffers;
458
+ var bsonRegExp =
459
+ typeof options.bsonRegExp === 'boolean' ? options.bsonRegExp : this.opts.bsonRegExp;
452
460
  var bsonSize, _options;
453
461
 
454
462
  // Set up the options
455
463
  _options = {
456
464
  promoteLongs: promoteLongs,
457
465
  promoteValues: promoteValues,
458
- promoteBuffers: promoteBuffers
466
+ promoteBuffers: promoteBuffers,
467
+ bsonRegExp: bsonRegExp
459
468
  };
460
469
 
461
470
  // Position within OP_REPLY at which documents start