mongodb 3.2.5 → 3.3.0-beta2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (133) hide show
  1. package/HISTORY.md +0 -10
  2. package/index.js +4 -4
  3. package/lib/admin.js +56 -56
  4. package/lib/aggregation_cursor.js +7 -3
  5. package/lib/bulk/common.js +18 -13
  6. package/lib/change_stream.js +196 -89
  7. package/lib/collection.js +217 -169
  8. package/lib/command_cursor.js +17 -7
  9. package/lib/core/auth/auth_provider.js +158 -0
  10. package/lib/core/auth/defaultAuthProviders.js +29 -0
  11. package/lib/core/auth/gssapi.js +241 -0
  12. package/lib/core/auth/mongo_credentials.js +81 -0
  13. package/lib/core/auth/mongocr.js +51 -0
  14. package/lib/core/auth/plain.js +35 -0
  15. package/lib/core/auth/scram.js +293 -0
  16. package/lib/core/auth/sspi.js +131 -0
  17. package/lib/core/auth/x509.js +26 -0
  18. package/lib/core/connection/apm.js +236 -0
  19. package/lib/core/connection/command_result.js +36 -0
  20. package/lib/core/connection/commands.js +507 -0
  21. package/lib/core/connection/connect.js +370 -0
  22. package/lib/core/connection/connection.js +624 -0
  23. package/lib/core/connection/logger.js +246 -0
  24. package/lib/core/connection/msg.js +219 -0
  25. package/lib/core/connection/pool.js +1285 -0
  26. package/lib/core/connection/utils.js +57 -0
  27. package/lib/core/cursor.js +752 -0
  28. package/lib/core/error.js +186 -0
  29. package/lib/core/index.js +50 -0
  30. package/lib/core/sdam/monitoring.js +228 -0
  31. package/lib/core/sdam/server.js +467 -0
  32. package/lib/core/sdam/server_description.js +163 -0
  33. package/lib/core/sdam/server_selectors.js +244 -0
  34. package/lib/core/sdam/srv_polling.js +135 -0
  35. package/lib/core/sdam/topology.js +1151 -0
  36. package/lib/core/sdam/topology_description.js +408 -0
  37. package/lib/core/sessions.js +711 -0
  38. package/lib/core/tools/smoke_plugin.js +61 -0
  39. package/lib/core/topologies/mongos.js +1337 -0
  40. package/lib/core/topologies/read_preference.js +202 -0
  41. package/lib/core/topologies/replset.js +1507 -0
  42. package/lib/core/topologies/replset_state.js +1121 -0
  43. package/lib/core/topologies/server.js +984 -0
  44. package/lib/core/topologies/shared.js +453 -0
  45. package/lib/core/transactions.js +167 -0
  46. package/lib/core/uri_parser.js +631 -0
  47. package/lib/core/utils.js +165 -0
  48. package/lib/core/wireprotocol/command.js +170 -0
  49. package/lib/core/wireprotocol/compression.js +73 -0
  50. package/lib/core/wireprotocol/constants.js +13 -0
  51. package/lib/core/wireprotocol/get_more.js +86 -0
  52. package/lib/core/wireprotocol/index.js +18 -0
  53. package/lib/core/wireprotocol/kill_cursors.js +70 -0
  54. package/lib/core/wireprotocol/query.js +224 -0
  55. package/lib/core/wireprotocol/shared.js +115 -0
  56. package/lib/core/wireprotocol/write_command.js +50 -0
  57. package/lib/cursor.js +40 -46
  58. package/lib/db.js +141 -95
  59. package/lib/dynamic_loaders.js +32 -0
  60. package/lib/error.js +12 -10
  61. package/lib/gridfs/chunk.js +2 -2
  62. package/lib/gridfs/grid_store.js +31 -25
  63. package/lib/gridfs-stream/index.js +4 -4
  64. package/lib/gridfs-stream/upload.js +1 -1
  65. package/lib/mongo_client.js +37 -15
  66. package/lib/operations/add_user.js +96 -0
  67. package/lib/operations/aggregate.js +24 -13
  68. package/lib/operations/aggregate_operation.js +127 -0
  69. package/lib/operations/bulk_write.js +104 -0
  70. package/lib/operations/close.js +47 -0
  71. package/lib/operations/collection_ops.js +28 -287
  72. package/lib/operations/collections.js +55 -0
  73. package/lib/operations/command.js +120 -0
  74. package/lib/operations/command_v2.js +43 -0
  75. package/lib/operations/common_functions.js +372 -0
  76. package/lib/operations/{mongo_client_ops.js → connect.js} +185 -157
  77. package/lib/operations/count.js +72 -0
  78. package/lib/operations/count_documents.js +46 -0
  79. package/lib/operations/create_collection.js +118 -0
  80. package/lib/operations/create_index.js +92 -0
  81. package/lib/operations/create_indexes.js +61 -0
  82. package/lib/operations/cursor_ops.js +3 -4
  83. package/lib/operations/db_ops.js +15 -12
  84. package/lib/operations/delete_many.js +25 -0
  85. package/lib/operations/delete_one.js +25 -0
  86. package/lib/operations/distinct.js +85 -0
  87. package/lib/operations/drop.js +53 -0
  88. package/lib/operations/drop_index.js +42 -0
  89. package/lib/operations/drop_indexes.js +23 -0
  90. package/lib/operations/estimated_document_count.js +33 -0
  91. package/lib/operations/execute_db_admin_command.js +34 -0
  92. package/lib/operations/execute_operation.js +165 -0
  93. package/lib/operations/explain.js +23 -0
  94. package/lib/operations/find_and_modify.js +98 -0
  95. package/lib/operations/find_one.js +33 -0
  96. package/lib/operations/find_one_and_delete.js +16 -0
  97. package/lib/operations/find_one_and_replace.js +18 -0
  98. package/lib/operations/find_one_and_update.js +19 -0
  99. package/lib/operations/geo_haystack_search.js +79 -0
  100. package/lib/operations/has_next.js +40 -0
  101. package/lib/operations/index_exists.js +39 -0
  102. package/lib/operations/index_information.js +23 -0
  103. package/lib/operations/indexes.js +22 -0
  104. package/lib/operations/insert_many.js +63 -0
  105. package/lib/operations/insert_one.js +75 -0
  106. package/lib/operations/is_capped.js +19 -0
  107. package/lib/operations/list_indexes.js +66 -0
  108. package/lib/operations/map_reduce.js +189 -0
  109. package/lib/operations/next.js +32 -0
  110. package/lib/operations/operation.js +63 -0
  111. package/lib/operations/options_operation.js +32 -0
  112. package/lib/operations/profiling_level.js +31 -0
  113. package/lib/operations/re_index.js +28 -0
  114. package/lib/operations/remove_user.js +52 -0
  115. package/lib/operations/rename.js +61 -0
  116. package/lib/operations/replace_one.js +47 -0
  117. package/lib/operations/set_profiling_level.js +48 -0
  118. package/lib/operations/stats.js +45 -0
  119. package/lib/operations/to_array.js +68 -0
  120. package/lib/operations/update_many.js +29 -0
  121. package/lib/operations/update_one.js +44 -0
  122. package/lib/operations/validate_collection.js +40 -0
  123. package/lib/read_concern.js +55 -0
  124. package/lib/topologies/mongos.js +3 -3
  125. package/lib/topologies/native_topology.js +22 -2
  126. package/lib/topologies/replset.js +3 -3
  127. package/lib/topologies/server.js +4 -4
  128. package/lib/topologies/topology_base.js +6 -6
  129. package/lib/url_parser.js +4 -3
  130. package/lib/utils.js +46 -59
  131. package/lib/write_concern.js +66 -0
  132. package/package.json +15 -6
  133. package/lib/.DS_Store +0 -0
@@ -0,0 +1,467 @@
1
+ 'use strict';
2
+ const EventEmitter = require('events');
3
+ const MongoError = require('../error').MongoError;
4
+ const Pool = require('../connection/pool');
5
+ const relayEvents = require('../utils').relayEvents;
6
+ const wireProtocol = require('../wireprotocol');
7
+ const BSON = require('../connection/utils').retrieveBSON();
8
+ const createClientInfo = require('../topologies/shared').createClientInfo;
9
+ const Logger = require('../connection/logger');
10
+ const ServerDescription = require('./server_description').ServerDescription;
11
+ const ReadPreference = require('../topologies/read_preference');
12
+ const monitorServer = require('./monitoring').monitorServer;
13
+ const MongoParseError = require('../error').MongoParseError;
14
+ const MongoNetworkError = require('../error').MongoNetworkError;
15
+ const collationNotSupported = require('../utils').collationNotSupported;
16
+ const debugOptions = require('../connection/utils').debugOptions;
17
+ const isSDAMUnrecoverableError = require('../error').isSDAMUnrecoverableError;
18
+
19
+ // Used for filtering out fields for logging
20
+ const DEBUG_FIELDS = [
21
+ 'reconnect',
22
+ 'reconnectTries',
23
+ 'reconnectInterval',
24
+ 'emitError',
25
+ 'cursorFactory',
26
+ 'host',
27
+ 'port',
28
+ 'size',
29
+ 'keepAlive',
30
+ 'keepAliveInitialDelay',
31
+ 'noDelay',
32
+ 'connectionTimeout',
33
+ 'checkServerIdentity',
34
+ 'socketTimeout',
35
+ 'ssl',
36
+ 'ca',
37
+ 'crl',
38
+ 'cert',
39
+ 'key',
40
+ 'rejectUnauthorized',
41
+ 'promoteLongs',
42
+ 'promoteValues',
43
+ 'promoteBuffers',
44
+ 'servername'
45
+ ];
46
+
47
+ const STATE_DISCONNECTED = 0;
48
+ const STATE_CONNECTING = 1;
49
+ const STATE_CONNECTED = 2;
50
+
51
+ /**
52
+ *
53
+ * @fires Server#serverHeartbeatStarted
54
+ * @fires Server#serverHeartbeatSucceeded
55
+ * @fires Server#serverHeartbeatFailed
56
+ */
57
+ class Server extends EventEmitter {
58
+ /**
59
+ * Create a server
60
+ *
61
+ * @param {ServerDescription} description
62
+ * @param {Object} options
63
+ */
64
+ constructor(description, options, topology) {
65
+ super();
66
+
67
+ this.s = {
68
+ // the server description
69
+ description,
70
+ // a saved copy of the incoming options
71
+ options,
72
+ // the server logger
73
+ logger: Logger('Server', options),
74
+ // the bson parser
75
+ bson: options.bson || new BSON(),
76
+ // client metadata for the initial handshake
77
+ clientInfo: createClientInfo(options),
78
+ // state variable to determine if there is an active server check in progress
79
+ monitoring: false,
80
+ // the implementation of the monitoring method
81
+ monitorFunction: options.monitorFunction || monitorServer,
82
+ // the connection pool
83
+ pool: null,
84
+ // the server state
85
+ state: STATE_DISCONNECTED,
86
+ credentials: options.credentials,
87
+ topology
88
+ };
89
+ }
90
+
91
+ get description() {
92
+ return this.s.description;
93
+ }
94
+
95
+ get name() {
96
+ return this.s.description.address;
97
+ }
98
+
99
+ get autoEncrypter() {
100
+ if (this.s.options && this.s.options.autoEncrypter) {
101
+ return this.s.options.autoEncrypter;
102
+ }
103
+ return null;
104
+ }
105
+
106
+ /**
107
+ * Initiate server connect
108
+ */
109
+ connect(options) {
110
+ options = options || {};
111
+
112
+ // do not allow connect to be called on anything that's not disconnected
113
+ if (this.s.pool && !this.s.pool.isDisconnected() && !this.s.pool.isDestroyed()) {
114
+ throw new MongoError(`Server instance in invalid state ${this.s.pool.state}`);
115
+ }
116
+
117
+ // create a pool
118
+ const addressParts = this.description.address.split(':');
119
+ const poolOptions = Object.assign(
120
+ { host: addressParts[0], port: parseInt(addressParts[1], 10) },
121
+ this.s.options,
122
+ options,
123
+ { bson: this.s.bson }
124
+ );
125
+
126
+ // NOTE: this should only be the case if we are connecting to a single server
127
+ poolOptions.reconnect = true;
128
+
129
+ this.s.pool = new Pool(this, poolOptions);
130
+
131
+ // setup listeners
132
+ this.s.pool.on('connect', connectEventHandler(this));
133
+ this.s.pool.on('close', errorEventHandler(this));
134
+ this.s.pool.on('error', errorEventHandler(this));
135
+ this.s.pool.on('parseError', parseErrorEventHandler(this));
136
+
137
+ // it is unclear whether consumers should even know about these events
138
+ // this.s.pool.on('timeout', timeoutEventHandler(this));
139
+ // this.s.pool.on('reconnect', reconnectEventHandler(this));
140
+ // this.s.pool.on('reconnectFailed', errorEventHandler(this));
141
+
142
+ // relay all command monitoring events
143
+ relayEvents(this.s.pool, this, ['commandStarted', 'commandSucceeded', 'commandFailed']);
144
+
145
+ this.s.state = STATE_CONNECTING;
146
+
147
+ // If auth settings have been provided, use them
148
+ if (options.auth) {
149
+ this.s.pool.connect.apply(this.s.pool, options.auth);
150
+ return;
151
+ }
152
+
153
+ this.s.pool.connect();
154
+ }
155
+
156
+ /**
157
+ * Destroy the server connection
158
+ *
159
+ * @param {Boolean} [options.force=false] Force destroy the pool
160
+ */
161
+ destroy(options, callback) {
162
+ if (typeof options === 'function') (callback = options), (options = {});
163
+ options = Object.assign({}, { force: false }, options);
164
+
165
+ const done = err => {
166
+ this.emit('closed');
167
+ this.s.state = STATE_DISCONNECTED;
168
+ if (typeof callback === 'function') {
169
+ callback(err, null);
170
+ }
171
+ };
172
+
173
+ if (!this.s.pool) {
174
+ return done();
175
+ }
176
+
177
+ ['close', 'error', 'timeout', 'parseError', 'connect'].forEach(event => {
178
+ this.s.pool.removeAllListeners(event);
179
+ });
180
+
181
+ if (this.s.monitorId) {
182
+ clearTimeout(this.s.monitorId);
183
+ }
184
+
185
+ this.s.pool.destroy(options.force, done);
186
+ }
187
+
188
+ /**
189
+ * Immediately schedule monitoring of this server. If there already an attempt being made
190
+ * this will be a no-op.
191
+ */
192
+ monitor(options) {
193
+ options = options || {};
194
+ if (this.s.state !== STATE_CONNECTED || this.s.monitoring) return;
195
+ if (this.s.monitorId) clearTimeout(this.s.monitorId);
196
+ this.s.monitorFunction(this, options);
197
+ }
198
+
199
+ /**
200
+ * Execute a command
201
+ *
202
+ * @param {string} ns The MongoDB fully qualified namespace (ex: db1.collection1)
203
+ * @param {object} cmd The command hash
204
+ * @param {ReadPreference} [options.readPreference] Specify read preference if command supports it
205
+ * @param {Boolean} [options.serializeFunctions=false] Specify if functions on an object should be serialized.
206
+ * @param {Boolean} [options.checkKeys=false] Specify if the bson parser should validate keys.
207
+ * @param {Boolean} [options.ignoreUndefined=false] Specify if the BSON serializer should ignore undefined fields.
208
+ * @param {Boolean} [options.fullResult=false] Return the full envelope instead of just the result document.
209
+ * @param {ClientSession} [options.session=null] Session to use for the operation
210
+ * @param {opResultCallback} callback A callback function
211
+ */
212
+ command(ns, cmd, options, callback) {
213
+ if (typeof options === 'function') {
214
+ (callback = options), (options = {}), (options = options || {});
215
+ }
216
+
217
+ const error = basicReadValidations(this, options);
218
+ if (error) {
219
+ return callback(error, null);
220
+ }
221
+
222
+ // Clone the options
223
+ options = Object.assign({}, options, { wireProtocolCommand: false });
224
+
225
+ // Debug log
226
+ if (this.s.logger.isDebug()) {
227
+ this.s.logger.debug(
228
+ `executing command [${JSON.stringify({
229
+ ns,
230
+ cmd,
231
+ options: debugOptions(DEBUG_FIELDS, options)
232
+ })}] against ${this.name}`
233
+ );
234
+ }
235
+
236
+ // error if collation not supported
237
+ if (collationNotSupported(this, cmd)) {
238
+ callback(new MongoError(`server ${this.name} does not support collation`));
239
+ return;
240
+ }
241
+
242
+ wireProtocol.command(this, ns, cmd, options, (err, result) => {
243
+ if (err && isSDAMUnrecoverableError(err)) {
244
+ this.emit('error', err);
245
+ }
246
+
247
+ callback(err, result);
248
+ });
249
+ }
250
+
251
+ /**
252
+ * Execute a query against the server
253
+ *
254
+ * @param {string} ns The MongoDB fully qualified namespace (ex: db1.collection1)
255
+ * @param {object} cmd The command document for the query
256
+ * @param {object} options Optional settings
257
+ * @param {function} callback
258
+ */
259
+ query(ns, cmd, cursorState, options, callback) {
260
+ wireProtocol.query(this, ns, cmd, cursorState, options, (err, result) => {
261
+ if (err && isSDAMUnrecoverableError(err)) {
262
+ this.emit('error', err);
263
+ }
264
+
265
+ callback(err, result);
266
+ });
267
+ }
268
+
269
+ /**
270
+ * Execute a `getMore` against the server
271
+ *
272
+ * @param {string} ns The MongoDB fully qualified namespace (ex: db1.collection1)
273
+ * @param {object} cursorState State data associated with the cursor calling this method
274
+ * @param {object} options Optional settings
275
+ * @param {function} callback
276
+ */
277
+ getMore(ns, cursorState, batchSize, options, callback) {
278
+ wireProtocol.getMore(this, ns, cursorState, batchSize, options, (err, result) => {
279
+ if (err && isSDAMUnrecoverableError(err)) {
280
+ this.emit('error', err);
281
+ }
282
+
283
+ callback(err, result);
284
+ });
285
+ }
286
+
287
+ /**
288
+ * Execute a `killCursors` command against the server
289
+ *
290
+ * @param {string} ns The MongoDB fully qualified namespace (ex: db1.collection1)
291
+ * @param {object} cursorState State data associated with the cursor calling this method
292
+ * @param {function} callback
293
+ */
294
+ killCursors(ns, cursorState, callback) {
295
+ wireProtocol.killCursors(this, ns, cursorState, (err, result) => {
296
+ if (err && isSDAMUnrecoverableError(err)) {
297
+ this.emit('error', err);
298
+ }
299
+
300
+ if (typeof callback === 'function') {
301
+ callback(err, result);
302
+ }
303
+ });
304
+ }
305
+
306
+ /**
307
+ * Insert one or more documents
308
+ * @method
309
+ * @param {string} ns The MongoDB fully qualified namespace (ex: db1.collection1)
310
+ * @param {array} ops An array of documents to insert
311
+ * @param {boolean} [options.ordered=true] Execute in order or out of order
312
+ * @param {object} [options.writeConcern={}] Write concern for the operation
313
+ * @param {Boolean} [options.serializeFunctions=false] Specify if functions on an object should be serialized.
314
+ * @param {Boolean} [options.ignoreUndefined=false] Specify if the BSON serializer should ignore undefined fields.
315
+ * @param {ClientSession} [options.session=null] Session to use for the operation
316
+ * @param {opResultCallback} callback A callback function
317
+ */
318
+ insert(ns, ops, options, callback) {
319
+ executeWriteOperation({ server: this, op: 'insert', ns, ops }, options, callback);
320
+ }
321
+
322
+ /**
323
+ * Perform one or more update operations
324
+ * @method
325
+ * @param {string} ns The MongoDB fully qualified namespace (ex: db1.collection1)
326
+ * @param {array} ops An array of updates
327
+ * @param {boolean} [options.ordered=true] Execute in order or out of order
328
+ * @param {object} [options.writeConcern={}] Write concern for the operation
329
+ * @param {Boolean} [options.serializeFunctions=false] Specify if functions on an object should be serialized.
330
+ * @param {Boolean} [options.ignoreUndefined=false] Specify if the BSON serializer should ignore undefined fields.
331
+ * @param {ClientSession} [options.session=null] Session to use for the operation
332
+ * @param {opResultCallback} callback A callback function
333
+ */
334
+ update(ns, ops, options, callback) {
335
+ executeWriteOperation({ server: this, op: 'update', ns, ops }, options, callback);
336
+ }
337
+
338
+ /**
339
+ * Perform one or more remove operations
340
+ * @method
341
+ * @param {string} ns The MongoDB fully qualified namespace (ex: db1.collection1)
342
+ * @param {array} ops An array of removes
343
+ * @param {boolean} [options.ordered=true] Execute in order or out of order
344
+ * @param {object} [options.writeConcern={}] Write concern for the operation
345
+ * @param {Boolean} [options.serializeFunctions=false] Specify if functions on an object should be serialized.
346
+ * @param {Boolean} [options.ignoreUndefined=false] Specify if the BSON serializer should ignore undefined fields.
347
+ * @param {ClientSession} [options.session=null] Session to use for the operation
348
+ * @param {opResultCallback} callback A callback function
349
+ */
350
+ remove(ns, ops, options, callback) {
351
+ executeWriteOperation({ server: this, op: 'remove', ns, ops }, options, callback);
352
+ }
353
+ }
354
+
355
+ Object.defineProperty(Server.prototype, 'clusterTime', {
356
+ get: function() {
357
+ return this.s.topology.clusterTime;
358
+ },
359
+ set: function(clusterTime) {
360
+ this.s.topology.clusterTime = clusterTime;
361
+ }
362
+ });
363
+
364
+ function basicWriteValidations(server) {
365
+ if (!server.s.pool) {
366
+ return new MongoError('server instance is not connected');
367
+ }
368
+
369
+ if (server.s.pool.isDestroyed()) {
370
+ return new MongoError('server instance pool was destroyed');
371
+ }
372
+
373
+ return null;
374
+ }
375
+
376
+ function basicReadValidations(server, options) {
377
+ const error = basicWriteValidations(server, options);
378
+ if (error) {
379
+ return error;
380
+ }
381
+
382
+ if (options.readPreference && !(options.readPreference instanceof ReadPreference)) {
383
+ return new MongoError('readPreference must be an instance of ReadPreference');
384
+ }
385
+ }
386
+
387
+ function executeWriteOperation(args, options, callback) {
388
+ if (typeof options === 'function') (callback = options), (options = {});
389
+ options = options || {};
390
+
391
+ // TODO: once we drop Node 4, use destructuring either here or in arguments.
392
+ const server = args.server;
393
+ const op = args.op;
394
+ const ns = args.ns;
395
+ const ops = Array.isArray(args.ops) ? args.ops : [args.ops];
396
+
397
+ const error = basicWriteValidations(server, options);
398
+ if (error) {
399
+ callback(error, null);
400
+ return;
401
+ }
402
+
403
+ if (collationNotSupported(server, options)) {
404
+ callback(new MongoError(`server ${this.name} does not support collation`));
405
+ return;
406
+ }
407
+
408
+ return wireProtocol[op](server, ns, ops, options, (err, result) => {
409
+ if (err && isSDAMUnrecoverableError(err)) {
410
+ server.emit('error', err);
411
+ }
412
+
413
+ callback(err, result);
414
+ });
415
+ }
416
+
417
+ function connectEventHandler(server) {
418
+ return function(pool, conn) {
419
+ const ismaster = conn.ismaster;
420
+ server.s.lastIsMasterMS = conn.lastIsMasterMS;
421
+ if (conn.agreedCompressor) {
422
+ server.s.pool.options.agreedCompressor = conn.agreedCompressor;
423
+ }
424
+
425
+ if (conn.zlibCompressionLevel) {
426
+ server.s.pool.options.zlibCompressionLevel = conn.zlibCompressionLevel;
427
+ }
428
+
429
+ if (conn.ismaster.$clusterTime) {
430
+ const $clusterTime = conn.ismaster.$clusterTime;
431
+ server.s.sclusterTime = $clusterTime;
432
+ }
433
+
434
+ // log the connection event if requested
435
+ if (server.s.logger.isInfo()) {
436
+ server.s.logger.info(
437
+ `server ${server.name} connected with ismaster [${JSON.stringify(ismaster)}]`
438
+ );
439
+ }
440
+
441
+ // emit an event indicating that our description has changed
442
+ server.emit('descriptionReceived', new ServerDescription(server.description.address, ismaster));
443
+
444
+ // we are connected and handshaked (guaranteed by the pool)
445
+ server.s.state = STATE_CONNECTED;
446
+ server.emit('connect', server);
447
+ };
448
+ }
449
+
450
+ function errorEventHandler(server) {
451
+ return function(err) {
452
+ if (err) {
453
+ server.emit('error', new MongoNetworkError(err));
454
+ }
455
+
456
+ server.emit('close');
457
+ };
458
+ }
459
+
460
+ function parseErrorEventHandler(server) {
461
+ return function(err) {
462
+ server.s.state = STATE_DISCONNECTED;
463
+ server.emit('error', new MongoParseError(err));
464
+ };
465
+ }
466
+
467
+ module.exports = Server;
@@ -0,0 +1,163 @@
1
+ 'use strict';
2
+
3
+ // An enumeration of server types we know about
4
+ const ServerType = {
5
+ Standalone: 'Standalone',
6
+ Mongos: 'Mongos',
7
+ PossiblePrimary: 'PossiblePrimary',
8
+ RSPrimary: 'RSPrimary',
9
+ RSSecondary: 'RSSecondary',
10
+ RSArbiter: 'RSArbiter',
11
+ RSOther: 'RSOther',
12
+ RSGhost: 'RSGhost',
13
+ Unknown: 'Unknown'
14
+ };
15
+
16
+ const WRITABLE_SERVER_TYPES = new Set([
17
+ ServerType.RSPrimary,
18
+ ServerType.Standalone,
19
+ ServerType.Mongos
20
+ ]);
21
+
22
+ const DATA_BEARING_SERVER_TYPES = new Set([
23
+ ServerType.RSPrimary,
24
+ ServerType.RSSecondary,
25
+ ServerType.Mongos,
26
+ ServerType.Standalone
27
+ ]);
28
+
29
+ const ISMASTER_FIELDS = [
30
+ 'minWireVersion',
31
+ 'maxWireVersion',
32
+ 'maxBsonObjectSize',
33
+ 'maxMessageSizeBytes',
34
+ 'maxWriteBatchSize',
35
+ 'compression',
36
+ 'me',
37
+ 'hosts',
38
+ 'passives',
39
+ 'arbiters',
40
+ 'tags',
41
+ 'setName',
42
+ 'setVersion',
43
+ 'electionId',
44
+ 'primary',
45
+ 'logicalSessionTimeoutMinutes',
46
+ 'saslSupportedMechs',
47
+ '__nodejs_mock_server__',
48
+ '$clusterTime'
49
+ ];
50
+
51
+ /**
52
+ * The client's view of a single server, based on the most recent ismaster outcome.
53
+ *
54
+ * Internal type, not meant to be directly instantiated
55
+ */
56
+ class ServerDescription {
57
+ /**
58
+ * Create a ServerDescription
59
+ * @param {String} address The address of the server
60
+ * @param {Object} [ismaster] An optional ismaster response for this server
61
+ * @param {Object} [options] Optional settings
62
+ * @param {Number} [options.roundTripTime] The round trip time to ping this server (in ms)
63
+ */
64
+ constructor(address, ismaster, options) {
65
+ options = options || {};
66
+ ismaster = Object.assign(
67
+ {
68
+ minWireVersion: 0,
69
+ maxWireVersion: 0,
70
+ hosts: [],
71
+ passives: [],
72
+ arbiters: [],
73
+ tags: []
74
+ },
75
+ ismaster
76
+ );
77
+
78
+ this.address = address;
79
+ this.error = options.error || null;
80
+ this.roundTripTime = options.roundTripTime || 0;
81
+ this.lastUpdateTime = Date.now();
82
+ this.lastWriteDate = ismaster.lastWrite ? ismaster.lastWrite.lastWriteDate : null;
83
+ this.opTime = ismaster.lastWrite ? ismaster.lastWrite.opTime : null;
84
+ this.type = parseServerType(ismaster);
85
+
86
+ // direct mappings
87
+ ISMASTER_FIELDS.forEach(field => {
88
+ if (typeof ismaster[field] !== 'undefined') this[field] = ismaster[field];
89
+ });
90
+
91
+ // normalize case for hosts
92
+ if (this.me) this.me = this.me.toLowerCase();
93
+ this.hosts = this.hosts.map(host => host.toLowerCase());
94
+ this.passives = this.passives.map(host => host.toLowerCase());
95
+ this.arbiters = this.arbiters.map(host => host.toLowerCase());
96
+ }
97
+
98
+ get allHosts() {
99
+ return this.hosts.concat(this.arbiters).concat(this.passives);
100
+ }
101
+
102
+ /**
103
+ * @return {Boolean} Is this server available for reads
104
+ */
105
+ get isReadable() {
106
+ return this.type === ServerType.RSSecondary || this.isWritable;
107
+ }
108
+
109
+ /**
110
+ * @return {Boolean} Is this server data bearing
111
+ */
112
+ get isDataBearing() {
113
+ return DATA_BEARING_SERVER_TYPES.has(this.type);
114
+ }
115
+
116
+ /**
117
+ * @return {Boolean} Is this server available for writes
118
+ */
119
+ get isWritable() {
120
+ return WRITABLE_SERVER_TYPES.has(this.type);
121
+ }
122
+ }
123
+
124
+ /**
125
+ * Parses an `ismaster` message and determines the server type
126
+ *
127
+ * @param {Object} ismaster The `ismaster` message to parse
128
+ * @return {ServerType}
129
+ */
130
+ function parseServerType(ismaster) {
131
+ if (!ismaster || !ismaster.ok) {
132
+ return ServerType.Unknown;
133
+ }
134
+
135
+ if (ismaster.isreplicaset) {
136
+ return ServerType.RSGhost;
137
+ }
138
+
139
+ if (ismaster.msg && ismaster.msg === 'isdbgrid') {
140
+ return ServerType.Mongos;
141
+ }
142
+
143
+ if (ismaster.setName) {
144
+ if (ismaster.hidden) {
145
+ return ServerType.RSOther;
146
+ } else if (ismaster.ismaster) {
147
+ return ServerType.RSPrimary;
148
+ } else if (ismaster.secondary) {
149
+ return ServerType.RSSecondary;
150
+ } else if (ismaster.arbiterOnly) {
151
+ return ServerType.RSArbiter;
152
+ } else {
153
+ return ServerType.RSOther;
154
+ }
155
+ }
156
+
157
+ return ServerType.Standalone;
158
+ }
159
+
160
+ module.exports = {
161
+ ServerDescription,
162
+ ServerType
163
+ };