mongodb 3.6.11 → 3.7.2

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 (40) hide show
  1. package/index.js +4 -0
  2. package/lib/cmap/connection.js +34 -7
  3. package/lib/cmap/connection_pool.js +4 -2
  4. package/lib/collection.js +1 -1
  5. package/lib/core/auth/mongo_credentials.js +4 -1
  6. package/lib/core/auth/mongodb_aws.js +16 -15
  7. package/lib/core/connection/connect.js +22 -5
  8. package/lib/core/connection/connection.js +2 -0
  9. package/lib/core/connection/pool.js +1 -0
  10. package/lib/core/connection/utils.js +35 -2
  11. package/lib/core/error.js +46 -46
  12. package/lib/core/index.js +9 -0
  13. package/lib/core/sdam/monitor.js +16 -2
  14. package/lib/core/sdam/server.js +2 -0
  15. package/lib/core/sdam/server_description.js +4 -0
  16. package/lib/core/sdam/topology.js +10 -2
  17. package/lib/core/sessions.js +11 -8
  18. package/lib/core/topologies/replset_state.js +5 -3
  19. package/lib/core/topologies/shared.js +4 -1
  20. package/lib/core/transactions.js +5 -1
  21. package/lib/core/uri_parser.js +14 -0
  22. package/lib/core/wireprotocol/command.js +24 -0
  23. package/lib/core/wireprotocol/kill_cursors.js +7 -2
  24. package/lib/core/wireprotocol/query.js +9 -5
  25. package/lib/cursor.js +4 -0
  26. package/lib/db.js +1 -0
  27. package/lib/error.js +21 -20
  28. package/lib/error_codes.js +36 -0
  29. package/lib/explain.js +5 -12
  30. package/lib/gridfs-stream/index.js +39 -24
  31. package/lib/gridfs-stream/upload.js +53 -46
  32. package/lib/mongo_client.js +30 -5
  33. package/lib/operations/connect.js +1 -0
  34. package/lib/operations/db_ops.js +10 -7
  35. package/lib/operations/estimated_document_count.js +47 -18
  36. package/lib/topologies/native_topology.js +4 -0
  37. package/lib/url_parser.js +8 -0
  38. package/lib/utils.js +10 -1
  39. package/package.json +3 -3
  40. package/HISTORY.md +0 -2908
@@ -34,7 +34,7 @@ var ReplSetState = function(options) {
34
34
  // Add event listener
35
35
  EventEmitter.call(this);
36
36
  // Topology state
37
- this.topologyType = TopologyType.ReplicaSetNoPrimary;
37
+ this.topologyType = options.setName ? TopologyType.ReplicaSetNoPrimary : TopologyType.Unknown;
38
38
  this.setName = options.setName;
39
39
 
40
40
  // Server set
@@ -218,7 +218,8 @@ const isArbiter = ismaster => ismaster.arbiterOnly && ismaster.setName;
218
218
  ReplSetState.prototype.update = function(server) {
219
219
  var self = this;
220
220
  // Get the current ismaster
221
- var ismaster = server.lastIsMaster();
221
+ const ismaster = server.lastIsMaster();
222
+ if (ismaster && ismaster.isWritablePrimary) ismaster.ismaster = ismaster.isWritablePrimary;
222
223
 
223
224
  // Get the server name and lowerCase it
224
225
  var serverName = server.name.toLowerCase();
@@ -358,7 +359,8 @@ ReplSetState.prototype.update = function(server) {
358
359
  // Standalone server, destroy and return
359
360
  //
360
361
  if (ismaster && ismaster.ismaster && !ismaster.setName) {
361
- this.topologyType = this.primary ? TopologyType.ReplicaSetWithPrimary : TopologyType.Unknown;
362
+ // We should not mark the topology as Unknown because of one standalone
363
+ // we should just remove this server from the set
362
364
  this.remove(server, { force: true });
363
365
  return false;
364
366
  }
@@ -1,11 +1,14 @@
1
1
  'use strict';
2
+
3
+ const MONGODB_ERROR_CODES = require('../../error_codes').MONGODB_ERROR_CODES;
2
4
  const ReadPreference = require('./read_preference');
3
5
  const TopologyType = require('../sdam/common').TopologyType;
4
6
  const MongoError = require('../error').MongoError;
5
7
  const isRetryableWriteError = require('../error').isRetryableWriteError;
6
8
  const maxWireVersion = require('../utils').maxWireVersion;
7
9
  const MongoNetworkError = require('../error').MongoNetworkError;
8
- const MMAPv1_RETRY_WRITES_ERROR_CODE = 20;
10
+
11
+ const MMAPv1_RETRY_WRITES_ERROR_CODE = MONGODB_ERROR_CODES.IllegalOperation;
9
12
 
10
13
  /**
11
14
  * Emit event if it exists
@@ -150,7 +150,11 @@ class Transaction {
150
150
  const nextStates = stateMachine[this.state];
151
151
  if (nextStates && nextStates.indexOf(nextState) !== -1) {
152
152
  this.state = nextState;
153
- if (this.state === TxnState.NO_TRANSACTION || this.state === TxnState.STARTING_TRANSACTION) {
153
+ if (
154
+ this.state === TxnState.NO_TRANSACTION ||
155
+ this.state === TxnState.STARTING_TRANSACTION ||
156
+ this.state === TxnState.TRANSACTION_ABORTED
157
+ ) {
154
158
  this.unpinServer();
155
159
  }
156
160
  return;
@@ -106,6 +106,11 @@ function parseSrvConnectionString(uri, options, callback) {
106
106
  }
107
107
 
108
108
  record = qs.parse(record[0].join(''));
109
+
110
+ if (Object.keys(record).some(k => k.toLowerCase() === 'loadbalanced')) {
111
+ return callback(new MongoParseError('Load balancer mode requires driver version 4+'));
112
+ }
113
+
109
114
  if (Object.keys(record).some(key => key !== 'authSource' && key !== 'replicaSet')) {
110
115
  return callback(
111
116
  new MongoParseError('Text record must only set `authSource` or `replicaSet`')
@@ -432,6 +437,11 @@ function parseQueryString(query, options) {
432
437
  }
433
438
 
434
439
  const normalizedKey = key.toLowerCase();
440
+ if (normalizedKey === 'serverapi') {
441
+ throw new MongoParseError(
442
+ 'URI cannot contain `serverApi`, it can only be passed to the client'
443
+ );
444
+ }
435
445
  const parsedValue = FILE_PATH_OPTIONS.has(normalizedKey)
436
446
  ? value
437
447
  : parseQueryStringItemValue(normalizedKey, value);
@@ -593,6 +603,10 @@ function parseConnectionString(uri, options, callback) {
593
603
 
594
604
  parsedOptions = Object.assign({}, parsedOptions, options);
595
605
 
606
+ if (Object.keys(parsedOptions).some(k => k.toLowerCase() === 'loadbalanced')) {
607
+ return callback(new MongoParseError('Load balancer mode requires driver version 4+'));
608
+ }
609
+
596
610
  if (protocol === PROTOCOL_MONGODB_SRV) {
597
611
  return parseSrvConnectionString(uri, parsedOptions, callback);
598
612
  }
@@ -48,6 +48,18 @@ function _command(server, ns, cmd, options, callback) {
48
48
  const serverClusterTime = server.clusterTime;
49
49
  let clusterTime = serverClusterTime;
50
50
  let finalCmd = Object.assign({}, cmd);
51
+
52
+ const serverApi = options.serverApi;
53
+ if (serverApi) {
54
+ finalCmd.apiVersion = serverApi.version || serverApi;
55
+ if (serverApi.strict != null) {
56
+ finalCmd.apiStrict = serverApi.strict;
57
+ }
58
+ if (serverApi.deprecationErrors != null) {
59
+ finalCmd.apiDeprecationErrors = serverApi.deprecationErrors;
60
+ }
61
+ }
62
+
51
63
  if (hasSessionSupport(server) && session) {
52
64
  const sessionClusterTime = session.clusterTime;
53
65
  if (
@@ -60,6 +72,18 @@ function _command(server, ns, cmd, options, callback) {
60
72
  clusterTime = sessionClusterTime;
61
73
  }
62
74
 
75
+ // We need to unpin any read or write commands that happen outside of a pinned
76
+ // transaction, so we check if we have a pinned transaction that is no longer
77
+ // active, and unpin for all except start or commit.
78
+ if (
79
+ !session.transaction.isActive &&
80
+ session.transaction.isPinned &&
81
+ !finalCmd.startTransaction &&
82
+ !finalCmd.commitTransaction
83
+ ) {
84
+ session.transaction.unpinServer();
85
+ }
86
+
63
87
  const err = applySession(session, finalCmd, options);
64
88
  if (err) {
65
89
  return callback(err);
@@ -8,8 +8,13 @@ const maxWireVersion = require('../utils').maxWireVersion;
8
8
  const emitWarning = require('../utils').emitWarning;
9
9
  const command = require('./command');
10
10
 
11
- function killCursors(server, ns, cursorState, callback) {
11
+ function killCursors(server, ns, cursorState, defaultOptions, callback) {
12
+ if (typeof defaultOptions === 'function') {
13
+ callback = defaultOptions;
14
+ defaultOptions = {};
15
+ }
12
16
  callback = typeof callback === 'function' ? callback : () => {};
17
+
13
18
  const cursorId = cursorState.cursorId;
14
19
 
15
20
  if (maxWireVersion(server) < 4) {
@@ -45,7 +50,7 @@ function killCursors(server, ns, cursorState, callback) {
45
50
  cursors: [cursorId]
46
51
  };
47
52
 
48
- const options = {};
53
+ const options = defaultOptions || {};
49
54
  if (typeof cursorState.session === 'object') options.session = cursorState.session;
50
55
 
51
56
  command(server, ns, killCursorCmd, options, (err, result) => {
@@ -37,9 +37,13 @@ function query(server, ns, cmd, cursorState, options, callback) {
37
37
 
38
38
  // If we have explain, we need to rewrite the find command
39
39
  // to wrap it in the explain command
40
- const explain = Explain.fromOptions(options);
41
- if (explain) {
42
- findCmd = decorateWithExplain(findCmd, explain);
40
+ try {
41
+ const explain = Explain.fromOptions(options);
42
+ if (explain) {
43
+ findCmd = decorateWithExplain(findCmd, explain);
44
+ }
45
+ } catch (err) {
46
+ return callback(err);
43
47
  }
44
48
 
45
49
  // NOTE: This actually modifies the passed in cmd, and our code _depends_ on this
@@ -140,8 +144,8 @@ function prepareFindCommand(server, ns, cmd, cursorState) {
140
144
  if (cmd.maxTimeMS) findCmd.maxTimeMS = cmd.maxTimeMS;
141
145
  if (cmd.min) findCmd.min = cmd.min;
142
146
  if (cmd.max) findCmd.max = cmd.max;
143
- findCmd.returnKey = cmd.returnKey ? cmd.returnKey : false;
144
- findCmd.showRecordId = cmd.showDiskLoc ? cmd.showDiskLoc : false;
147
+ if (typeof cmd.returnKey === 'boolean') findCmd.returnKey = cmd.returnKey;
148
+ if (typeof cmd.showDiskLoc === 'boolean') findCmd.showRecordId = cmd.showDiskLoc;
145
149
  if (cmd.snapshot) findCmd.snapshot = cmd.snapshot;
146
150
  if (cmd.tailable) findCmd.tailable = cmd.tailable;
147
151
  if (cmd.oplogReplay) findCmd.oplogReplay = cmd.oplogReplay;
package/lib/cursor.js CHANGED
@@ -165,6 +165,10 @@ class Cursor extends CoreCursor {
165
165
  return this.cmd.sort;
166
166
  }
167
167
 
168
+ set session(clientSession) {
169
+ this.cursorState.session = clientSession;
170
+ }
171
+
168
172
  _initializeCursor(callback) {
169
173
  if (this.operation && this.operation.session != null) {
170
174
  this.cursorState.session = this.operation.session;
package/lib/db.js CHANGED
@@ -951,6 +951,7 @@ Db.prototype.indexInformation = function(name, options, callback) {
951
951
  /**
952
952
  * Unref all sockets
953
953
  * @method
954
+ * @deprecated This function is deprecated and will be removed in the next major version.
954
955
  */
955
956
  Db.prototype.unref = function() {
956
957
  this.s.topology.unref();
package/lib/error.js CHANGED
@@ -1,27 +1,28 @@
1
1
  'use strict';
2
2
 
3
3
  const MongoNetworkError = require('./core').MongoNetworkError;
4
+ const MONGODB_ERROR_CODES = require('./error_codes').MONGODB_ERROR_CODES;
4
5
 
5
6
  // From spec@https://github.com/mongodb/specifications/blob/f93d78191f3db2898a59013a7ed5650352ef6da8/source/change-streams/change-streams.rst#resumable-error
6
7
  const GET_MORE_RESUMABLE_CODES = new Set([
7
- 6, // HostUnreachable
8
- 7, // HostNotFound
9
- 89, // NetworkTimeout
10
- 91, // ShutdownInProgress
11
- 189, // PrimarySteppedDown
12
- 262, // ExceededTimeLimit
13
- 9001, // SocketException
14
- 10107, // NotMaster
15
- 11600, // InterruptedAtShutdown
16
- 11602, // InterruptedDueToReplStateChange
17
- 13435, // NotMasterNoSlaveOk
18
- 13436, // NotMasterOrSecondary
19
- 63, // StaleShardVersion
20
- 150, // StaleEpoch
21
- 13388, // StaleConfig
22
- 234, // RetryChangeStream
23
- 133, // FailedToSatisfyReadPreference
24
- 43 // CursorNotFound
8
+ MONGODB_ERROR_CODES.HostUnreachable,
9
+ MONGODB_ERROR_CODES.HostNotFound,
10
+ MONGODB_ERROR_CODES.NetworkTimeout,
11
+ MONGODB_ERROR_CODES.ShutdownInProgress,
12
+ MONGODB_ERROR_CODES.PrimarySteppedDown,
13
+ MONGODB_ERROR_CODES.ExceededTimeLimit,
14
+ MONGODB_ERROR_CODES.SocketException,
15
+ MONGODB_ERROR_CODES.NotMaster,
16
+ MONGODB_ERROR_CODES.InterruptedAtShutdown,
17
+ MONGODB_ERROR_CODES.InterruptedDueToReplStateChange,
18
+ MONGODB_ERROR_CODES.NotMasterNoSlaveOk,
19
+ MONGODB_ERROR_CODES.NotMasterOrSecondary,
20
+ MONGODB_ERROR_CODES.StaleShardVersion,
21
+ MONGODB_ERROR_CODES.StaleEpoch,
22
+ MONGODB_ERROR_CODES.StaleConfig,
23
+ MONGODB_ERROR_CODES.RetryChangeStream,
24
+ MONGODB_ERROR_CODES.FailedToSatisfyReadPreference,
25
+ MONGODB_ERROR_CODES.CursorNotFound
25
26
  ]);
26
27
 
27
28
  function isResumableError(error, wireVersion) {
@@ -31,7 +32,7 @@ function isResumableError(error, wireVersion) {
31
32
 
32
33
  if (wireVersion >= 9) {
33
34
  // DRIVERS-1308: For 4.4 drivers running against 4.4 servers, drivers will add a special case to treat the CursorNotFound error code as resumable
34
- if (error.code === 43) {
35
+ if (error.code === MONGODB_ERROR_CODES.CursorNotFound) {
35
36
  return true;
36
37
  }
37
38
  return error.hasErrorLabel('ResumableChangeStreamError');
@@ -40,4 +41,4 @@ function isResumableError(error, wireVersion) {
40
41
  return GET_MORE_RESUMABLE_CODES.has(error.code);
41
42
  }
42
43
 
43
- module.exports = { GET_MORE_RESUMABLE_CODES, isResumableError };
44
+ module.exports = { GET_MORE_RESUMABLE_CODES, isResumableError, MONGODB_ERROR_CODES };
@@ -0,0 +1,36 @@
1
+ 'use strict';
2
+
3
+ const MONGODB_ERROR_CODES = Object.freeze({
4
+ HostUnreachable: 6,
5
+ HostNotFound: 7,
6
+ NetworkTimeout: 89,
7
+ ShutdownInProgress: 91,
8
+ PrimarySteppedDown: 189,
9
+ ExceededTimeLimit: 262,
10
+ SocketException: 9001,
11
+ NotMaster: 10107,
12
+ InterruptedAtShutdown: 11600,
13
+ InterruptedDueToReplStateChange: 11602,
14
+ NotMasterNoSlaveOk: 13435,
15
+ NotMasterOrSecondary: 13436,
16
+ StaleShardVersion: 63,
17
+ StaleEpoch: 150,
18
+ StaleConfig: 13388,
19
+ RetryChangeStream: 234,
20
+ FailedToSatisfyReadPreference: 133,
21
+ CursorNotFound: 43,
22
+ LegacyNotPrimary: 10058,
23
+ WriteConcernFailed: 64,
24
+ NamespaceNotFound: 26,
25
+ IllegalOperation: 20,
26
+ MaxTimeMSExpired: 50,
27
+ UnknownReplWriteConcern: 79,
28
+ UnsatisfiableWriteConcern: 100,
29
+ DuplicateKey: 11000,
30
+ CannotCreateIndex: 67,
31
+ IndexOptionsConflict: 85,
32
+ IndexKeySpecsConflict: 86,
33
+ InvalidIndexSpecificationOption: 197
34
+ });
35
+
36
+ module.exports = Object.freeze({ MONGODB_ERROR_CODES });
package/lib/explain.js CHANGED
@@ -2,16 +2,9 @@
2
2
 
3
3
  const MongoError = require('./core/error').MongoError;
4
4
 
5
- const ExplainVerbosity = {
6
- queryPlanner: 'queryPlanner',
7
- queryPlannerExtended: 'queryPlannerExtended',
8
- executionStats: 'executionStats',
9
- allPlansExecution: 'allPlansExecution'
10
- };
11
-
12
5
  /**
13
6
  * @class
14
- * @property {'queryPlanner'|'queryPlannerExtended'|'executionStats'|'allPlansExecution'} verbosity The verbosity mode for the explain output.
7
+ * @property {string} verbosity The verbosity mode for the explain output, e.g.: 'queryPlanner', 'queryPlannerExtended', 'executionStats', 'allPlansExecution'.
15
8
  */
16
9
  class Explain {
17
10
  /**
@@ -21,7 +14,7 @@ class Explain {
21
14
  * and false as "queryPlanner". Prior to server version 3.6, aggregate()
22
15
  * ignores the verbosity parameter and executes in "queryPlanner".
23
16
  *
24
- * @param {'queryPlanner'|'queryPlannerExtended'|'executionStats'|'allPlansExecution'|boolean} [verbosity] The verbosity mode for the explain output.
17
+ * @param {string|boolean} [verbosity] The verbosity mode for the explain output.
25
18
  */
26
19
  constructor(verbosity) {
27
20
  if (typeof verbosity === 'boolean') {
@@ -35,7 +28,7 @@ class Explain {
35
28
  * Construct an Explain given an options object.
36
29
  *
37
30
  * @param {object} [options] The options object from which to extract the explain.
38
- * @param {'queryPlanner'|'queryPlannerExtended'|'executionStats'|'allPlansExecution'|boolean} [options.explain] The verbosity mode for the explain output
31
+ * @param {string|boolean} [options.explain] The verbosity mode for the explain output.
39
32
  * @return {Explain}
40
33
  */
41
34
  static fromOptions(options) {
@@ -44,11 +37,11 @@ class Explain {
44
37
  }
45
38
 
46
39
  const explain = options.explain;
47
- if (typeof explain === 'boolean' || explain in ExplainVerbosity) {
40
+ if (typeof explain === 'boolean' || typeof explain === 'string') {
48
41
  return new Explain(options.explain);
49
42
  }
50
43
 
51
- throw new MongoError(`explain must be one of ${Object.keys(ExplainVerbosity)} or a boolean`);
44
+ throw new MongoError(`explain must be a string or a boolean`);
52
45
  }
53
46
  }
54
47
 
@@ -7,6 +7,7 @@ var shallowClone = require('../utils').shallowClone;
7
7
  var toError = require('../utils').toError;
8
8
  var util = require('util');
9
9
  var executeLegacyOperation = require('../utils').executeLegacyOperation;
10
+ const deprecateOptions = require('../utils').deprecateOptions;
10
11
 
11
12
  var DEFAULT_GRIDFS_BUCKET_OPTIONS = {
12
13
  bucketName: 'fs',
@@ -79,21 +80,28 @@ util.inherits(GridFSBucket, Emitter);
79
80
  * @param {object} [options.metadata] Optional object to store in the file document's `metadata` field
80
81
  * @param {string} [options.contentType] Optional string to store in the file document's `contentType` field
81
82
  * @param {array} [options.aliases] Optional array of strings to store in the file document's `aliases` field
82
- * @param {boolean} [options.disableMD5=false] If true, disables adding an md5 field to file data
83
+ * @param {boolean} [options.disableMD5=false] **Deprecated** If true, disables adding an md5 field to file data
83
84
  * @return {GridFSBucketWriteStream}
84
85
  */
85
86
 
86
- GridFSBucket.prototype.openUploadStream = function(filename, options) {
87
- if (options) {
88
- options = shallowClone(options);
89
- } else {
90
- options = {};
91
- }
92
- if (!options.chunkSizeBytes) {
93
- options.chunkSizeBytes = this.s.options.chunkSizeBytes;
87
+ GridFSBucket.prototype.openUploadStream = deprecateOptions(
88
+ {
89
+ name: 'GridFSBucket.openUploadStream',
90
+ deprecatedOptions: ['disableMD5'],
91
+ optionsIndex: 1
92
+ },
93
+ function(filename, options) {
94
+ if (options) {
95
+ options = shallowClone(options);
96
+ } else {
97
+ options = {};
98
+ }
99
+ if (!options.chunkSizeBytes) {
100
+ options.chunkSizeBytes = this.s.options.chunkSizeBytes;
101
+ }
102
+ return new GridFSBucketWriteStream(this, filename, options);
94
103
  }
95
- return new GridFSBucketWriteStream(this, filename, options);
96
- };
104
+ );
97
105
 
98
106
  /**
99
107
  * Returns a writable stream (GridFSBucketWriteStream) for writing
@@ -107,25 +115,32 @@ GridFSBucket.prototype.openUploadStream = function(filename, options) {
107
115
  * @param {object} [options.metadata] Optional object to store in the file document's `metadata` field
108
116
  * @param {string} [options.contentType] Optional string to store in the file document's `contentType` field
109
117
  * @param {array} [options.aliases] Optional array of strings to store in the file document's `aliases` field
110
- * @param {boolean} [options.disableMD5=false] If true, disables adding an md5 field to file data
118
+ * @param {boolean} [options.disableMD5=false] **Deprecated** If true, disables adding an md5 field to file data
111
119
  * @return {GridFSBucketWriteStream}
112
120
  */
113
121
 
114
- GridFSBucket.prototype.openUploadStreamWithId = function(id, filename, options) {
115
- if (options) {
116
- options = shallowClone(options);
117
- } else {
118
- options = {};
119
- }
122
+ GridFSBucket.prototype.openUploadStreamWithId = deprecateOptions(
123
+ {
124
+ name: 'GridFSBucket.openUploadStreamWithId',
125
+ deprecatedOptions: ['disableMD5'],
126
+ optionsIndex: 2
127
+ },
128
+ function(id, filename, options) {
129
+ if (options) {
130
+ options = shallowClone(options);
131
+ } else {
132
+ options = {};
133
+ }
120
134
 
121
- if (!options.chunkSizeBytes) {
122
- options.chunkSizeBytes = this.s.options.chunkSizeBytes;
123
- }
135
+ if (!options.chunkSizeBytes) {
136
+ options.chunkSizeBytes = this.s.options.chunkSizeBytes;
137
+ }
124
138
 
125
- options.id = id;
139
+ options.id = id;
126
140
 
127
- return new GridFSBucketWriteStream(this, filename, options);
128
- };
141
+ return new GridFSBucketWriteStream(this, filename, options);
142
+ }
143
+ );
129
144
 
130
145
  /**
131
146
  * Returns a readable stream (GridFSBucketReadStream) for streaming file
@@ -1,14 +1,12 @@
1
1
  'use strict';
2
2
 
3
- var core = require('../core');
4
- var crypto = require('crypto');
5
- var stream = require('stream');
6
- var util = require('util');
7
- var Buffer = require('safe-buffer').Buffer;
8
-
9
- var ERROR_NAMESPACE_NOT_FOUND = 26;
10
-
11
- module.exports = GridFSBucketWriteStream;
3
+ const MONGODB_ERROR_CODES = require('../error_codes').MONGODB_ERROR_CODES;
4
+ const core = require('../core');
5
+ const crypto = require('crypto');
6
+ const stream = require('stream');
7
+ const util = require('util');
8
+ const Buffer = require('safe-buffer').Buffer;
9
+ const deprecateOptions = require('../utils').deprecateOptions;
12
10
 
13
11
  /**
14
12
  * A writable stream that enables you to write buffers to GridFS.
@@ -31,42 +29,49 @@ module.exports = GridFSBucketWriteStream;
31
29
  * @fires GridFSBucketWriteStream#finish
32
30
  */
33
31
 
34
- function GridFSBucketWriteStream(bucket, filename, options) {
35
- options = options || {};
36
- stream.Writable.call(this, options);
37
- this.bucket = bucket;
38
- this.chunks = bucket.s._chunksCollection;
39
- this.filename = filename;
40
- this.files = bucket.s._filesCollection;
41
- this.options = options;
42
- // Signals the write is all done
43
- this.done = false;
44
-
45
- this.id = options.id ? options.id : core.BSON.ObjectId();
46
- this.chunkSizeBytes = this.options.chunkSizeBytes;
47
- this.bufToStore = Buffer.alloc(this.chunkSizeBytes);
48
- this.length = 0;
49
- this.md5 = !options.disableMD5 && crypto.createHash('md5');
50
- this.n = 0;
51
- this.pos = 0;
52
- this.state = {
53
- streamEnd: false,
54
- outstandingRequests: 0,
55
- errored: false,
56
- aborted: false,
57
- promiseLibrary: this.bucket.s.promiseLibrary
58
- };
59
-
60
- if (!this.bucket.s.calledOpenUploadStream) {
61
- this.bucket.s.calledOpenUploadStream = true;
62
-
63
- var _this = this;
64
- checkIndexes(this, function() {
65
- _this.bucket.s.checkedIndexes = true;
66
- _this.bucket.emit('index');
67
- });
32
+ const GridFSBucketWriteStream = deprecateOptions(
33
+ {
34
+ name: 'GridFSBucketWriteStream',
35
+ deprecatedOptions: ['disableMD5'],
36
+ optionsIndex: 2
37
+ },
38
+ function(bucket, filename, options) {
39
+ options = options || {};
40
+ stream.Writable.call(this, options);
41
+ this.bucket = bucket;
42
+ this.chunks = bucket.s._chunksCollection;
43
+ this.filename = filename;
44
+ this.files = bucket.s._filesCollection;
45
+ this.options = options;
46
+ // Signals the write is all done
47
+ this.done = false;
48
+
49
+ this.id = options.id ? options.id : core.BSON.ObjectId();
50
+ this.chunkSizeBytes = this.options.chunkSizeBytes;
51
+ this.bufToStore = Buffer.alloc(this.chunkSizeBytes);
52
+ this.length = 0;
53
+ this.md5 = !options.disableMD5 && crypto.createHash('md5');
54
+ this.n = 0;
55
+ this.pos = 0;
56
+ this.state = {
57
+ streamEnd: false,
58
+ outstandingRequests: 0,
59
+ errored: false,
60
+ aborted: false,
61
+ promiseLibrary: this.bucket.s.promiseLibrary
62
+ };
63
+
64
+ if (!this.bucket.s.calledOpenUploadStream) {
65
+ this.bucket.s.calledOpenUploadStream = true;
66
+
67
+ var _this = this;
68
+ checkIndexes(this, function() {
69
+ _this.bucket.s.checkedIndexes = true;
70
+ _this.bucket.emit('index');
71
+ });
72
+ }
68
73
  }
69
- }
74
+ );
70
75
 
71
76
  util.inherits(GridFSBucketWriteStream, stream.Writable);
72
77
 
@@ -210,7 +215,7 @@ function checkChunksIndex(_this, callback) {
210
215
  _this.chunks.listIndexes().toArray(function(error, indexes) {
211
216
  if (error) {
212
217
  // Collection doesn't exist so create index
213
- if (error.code === ERROR_NAMESPACE_NOT_FOUND) {
218
+ if (error.code === MONGODB_ERROR_CODES.NamespaceNotFound) {
214
219
  var index = { files_id: 1, n: 1 };
215
220
  _this.chunks.createIndex(index, { background: false, unique: true }, function(error) {
216
221
  if (error) {
@@ -309,7 +314,7 @@ function checkIndexes(_this, callback) {
309
314
  _this.files.listIndexes().toArray(function(error, indexes) {
310
315
  if (error) {
311
316
  // Collection doesn't exist so create index
312
- if (error.code === ERROR_NAMESPACE_NOT_FOUND) {
317
+ if (error.code === MONGODB_ERROR_CODES.NamespaceNotFound) {
313
318
  var index = { filename: 1, uploadDate: 1 };
314
319
  _this.files.createIndex(index, { background: false }, function(error) {
315
320
  if (error) {
@@ -539,3 +544,5 @@ function checkAborted(_this, callback) {
539
544
  }
540
545
  return false;
541
546
  }
547
+
548
+ module.exports = GridFSBucketWriteStream;
@@ -5,6 +5,7 @@ const Db = require('./db');
5
5
  const EventEmitter = require('events').EventEmitter;
6
6
  const inherits = require('util').inherits;
7
7
  const MongoError = require('./core').MongoError;
8
+ const ValidServerApiVersions = require('./core').ValidServerApiVersions;
8
9
  const deprecate = require('util').deprecate;
9
10
  const WriteConcern = require('./write_concern');
10
11
  const MongoDBNamespace = require('./utils').MongoDBNamespace;
@@ -156,6 +157,7 @@ const validOptions = require('./operations/connect').validOptions;
156
157
  * @property {number} [numberOfRetries] (**default**: 5) The number of retries for a tailable cursor
157
158
  * @property {boolean} [auto_reconnect] (**default**: true) Enable auto reconnecting for single server instances
158
159
  * @property {boolean} [monitorCommands] (**default**: false) Enable command monitoring for this client
160
+ * @property {string|ServerApi} [serverApi] (**default**: undefined) The server API version
159
161
  * @property {number} [minSize] If present, the connection pool will be initialized with minSize connections, and will never dip below minSize connections
160
162
  * @property {boolean} [useNewUrlParser] (**default**: true) Determines whether or not to use the new url parser. Enables the new, spec-compliant, url parser shipped in the core driver. This url parser fixes a number of problems with the original parser, and aims to outright replace that parser in the near future. Defaults to true, and must be explicitly set to false to use the legacy url parser.
161
163
  * @property {boolean} [useUnifiedTopology] Enables the new unified topology layer
@@ -191,16 +193,38 @@ const validOptions = require('./operations/connect').validOptions;
191
193
  * @param {MongoClientOptions} [options] Optional settings
192
194
  */
193
195
  function MongoClient(url, options) {
196
+ options = options || {};
194
197
  if (!(this instanceof MongoClient)) return new MongoClient(url, options);
195
198
  // Set up event emitter
196
199
  EventEmitter.call(this);
197
200
 
198
- if (options && options.autoEncryption) require('./encrypter'); // Does CSFLE lib check
201
+ if (options.autoEncryption) require('./encrypter'); // Does CSFLE lib check
202
+
203
+ if (options.serverApi) {
204
+ const serverApiToValidate =
205
+ typeof options.serverApi === 'string' ? { version: options.serverApi } : options.serverApi;
206
+ const versionToValidate = serverApiToValidate && serverApiToValidate.version;
207
+ if (!versionToValidate) {
208
+ throw new MongoError(
209
+ `Invalid \`serverApi\` property; must specify a version from the following enum: ["${ValidServerApiVersions.join(
210
+ '", "'
211
+ )}"]`
212
+ );
213
+ }
214
+ if (!ValidServerApiVersions.some(v => v === versionToValidate)) {
215
+ throw new MongoError(
216
+ `Invalid server API version=${versionToValidate}; must be in the following enum: ["${ValidServerApiVersions.join(
217
+ '", "'
218
+ )}"]`
219
+ );
220
+ }
221
+ options.serverApi = serverApiToValidate;
222
+ }
199
223
 
200
224
  // The internal state
201
225
  this.s = {
202
- url: url,
203
- options: options || {},
226
+ url,
227
+ options,
204
228
  promiseLibrary: (options && options.promiseLibrary) || Promise,
205
229
  dbCache: new Map(),
206
230
  sessions: new Set(),
@@ -364,17 +388,18 @@ MongoClient.prototype.db = function(dbName, options) {
364
388
  * Check if MongoClient is connected
365
389
  *
366
390
  * @method
391
+ * @deprecated
367
392
  * @param {object} [options] Optional settings.
368
393
  * @param {boolean} [options.noListener=false] Do not make the db an event listener to the original connection.
369
394
  * @param {boolean} [options.returnNonCachedInstance=false] Control if you want to return a cached instance or have a new one created
370
395
  * @return {boolean}
371
396
  */
372
- MongoClient.prototype.isConnected = function(options) {
397
+ MongoClient.prototype.isConnected = deprecate(function(options) {
373
398
  options = options || {};
374
399
 
375
400
  if (!this.topology) return false;
376
401
  return this.topology.isConnected(options);
377
- };
402
+ }, 'isConnected is deprecated and will be removed in the next major version');
378
403
 
379
404
  /**
380
405
  * Connect to MongoDB using a url as documented at