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,293 @@
1
+ 'use strict';
2
+
3
+ const crypto = require('crypto');
4
+ const Buffer = require('safe-buffer').Buffer;
5
+ const retrieveBSON = require('../connection/utils').retrieveBSON;
6
+ const MongoError = require('../error').MongoError;
7
+ const AuthProvider = require('./auth_provider').AuthProvider;
8
+
9
+ const BSON = retrieveBSON();
10
+ const Binary = BSON.Binary;
11
+
12
+ let saslprep;
13
+ try {
14
+ saslprep = require('saslprep');
15
+ } catch (e) {
16
+ // don't do anything;
17
+ }
18
+
19
+ var parsePayload = function(payload) {
20
+ var dict = {};
21
+ var parts = payload.split(',');
22
+
23
+ for (var i = 0; i < parts.length; i++) {
24
+ var valueParts = parts[i].split('=');
25
+ dict[valueParts[0]] = valueParts[1];
26
+ }
27
+
28
+ return dict;
29
+ };
30
+
31
+ var passwordDigest = function(username, password) {
32
+ if (typeof username !== 'string') throw new MongoError('username must be a string');
33
+ if (typeof password !== 'string') throw new MongoError('password must be a string');
34
+ if (password.length === 0) throw new MongoError('password cannot be empty');
35
+ // Use node md5 generator
36
+ var md5 = crypto.createHash('md5');
37
+ // Generate keys used for authentication
38
+ md5.update(username + ':mongo:' + password, 'utf8');
39
+ return md5.digest('hex');
40
+ };
41
+
42
+ // XOR two buffers
43
+ function xor(a, b) {
44
+ if (!Buffer.isBuffer(a)) a = Buffer.from(a);
45
+ if (!Buffer.isBuffer(b)) b = Buffer.from(b);
46
+ const length = Math.max(a.length, b.length);
47
+ const res = [];
48
+
49
+ for (let i = 0; i < length; i += 1) {
50
+ res.push(a[i] ^ b[i]);
51
+ }
52
+
53
+ return Buffer.from(res).toString('base64');
54
+ }
55
+
56
+ function H(method, text) {
57
+ return crypto
58
+ .createHash(method)
59
+ .update(text)
60
+ .digest();
61
+ }
62
+
63
+ function HMAC(method, key, text) {
64
+ return crypto
65
+ .createHmac(method, key)
66
+ .update(text)
67
+ .digest();
68
+ }
69
+
70
+ var _hiCache = {};
71
+ var _hiCacheCount = 0;
72
+ var _hiCachePurge = function() {
73
+ _hiCache = {};
74
+ _hiCacheCount = 0;
75
+ };
76
+
77
+ const hiLengthMap = {
78
+ sha256: 32,
79
+ sha1: 20
80
+ };
81
+
82
+ function HI(data, salt, iterations, cryptoMethod) {
83
+ // omit the work if already generated
84
+ const key = [data, salt.toString('base64'), iterations].join('_');
85
+ if (_hiCache[key] !== undefined) {
86
+ return _hiCache[key];
87
+ }
88
+
89
+ // generate the salt
90
+ const saltedData = crypto.pbkdf2Sync(
91
+ data,
92
+ salt,
93
+ iterations,
94
+ hiLengthMap[cryptoMethod],
95
+ cryptoMethod
96
+ );
97
+
98
+ // cache a copy to speed up the next lookup, but prevent unbounded cache growth
99
+ if (_hiCacheCount >= 200) {
100
+ _hiCachePurge();
101
+ }
102
+
103
+ _hiCache[key] = saltedData;
104
+ _hiCacheCount += 1;
105
+ return saltedData;
106
+ }
107
+
108
+ /**
109
+ * Creates a new ScramSHA authentication mechanism
110
+ * @class
111
+ * @extends AuthProvider
112
+ */
113
+ class ScramSHA extends AuthProvider {
114
+ constructor(bson, cryptoMethod) {
115
+ super(bson);
116
+ this.cryptoMethod = cryptoMethod || 'sha1';
117
+ }
118
+
119
+ static _getError(err, r) {
120
+ if (err) {
121
+ return err;
122
+ }
123
+
124
+ if (r.$err || r.errmsg) {
125
+ return new MongoError(r);
126
+ }
127
+ }
128
+
129
+ /**
130
+ * @ignore
131
+ */
132
+ _executeScram(sendAuthCommand, connection, credentials, nonce, callback) {
133
+ let username = credentials.username;
134
+ const password = credentials.password;
135
+ const db = credentials.source;
136
+
137
+ const cryptoMethod = this.cryptoMethod;
138
+ let mechanism = 'SCRAM-SHA-1';
139
+ let processedPassword;
140
+
141
+ if (cryptoMethod === 'sha256') {
142
+ mechanism = 'SCRAM-SHA-256';
143
+
144
+ processedPassword = saslprep ? saslprep(password) : password;
145
+ } else {
146
+ try {
147
+ processedPassword = passwordDigest(username, password);
148
+ } catch (e) {
149
+ return callback(e);
150
+ }
151
+ }
152
+
153
+ // Clean up the user
154
+ username = username.replace('=', '=3D').replace(',', '=2C');
155
+
156
+ // NOTE: This is done b/c Javascript uses UTF-16, but the server is hashing in UTF-8.
157
+ // Since the username is not sasl-prep-d, we need to do this here.
158
+ const firstBare = Buffer.concat([
159
+ Buffer.from('n=', 'utf8'),
160
+ Buffer.from(username, 'utf8'),
161
+ Buffer.from(',r=', 'utf8'),
162
+ Buffer.from(nonce, 'utf8')
163
+ ]);
164
+
165
+ // Build command structure
166
+ const saslStartCmd = {
167
+ saslStart: 1,
168
+ mechanism,
169
+ payload: new Binary(Buffer.concat([Buffer.from('n,,', 'utf8'), firstBare])),
170
+ autoAuthorize: 1
171
+ };
172
+
173
+ // Write the commmand on the connection
174
+ sendAuthCommand(connection, `${db}.$cmd`, saslStartCmd, (err, r) => {
175
+ let tmpError = ScramSHA._getError(err, r);
176
+ if (tmpError) {
177
+ return callback(tmpError, null);
178
+ }
179
+
180
+ const payload = Buffer.isBuffer(r.payload) ? new Binary(r.payload) : r.payload;
181
+ const dict = parsePayload(payload.value());
182
+ const iterations = parseInt(dict.i, 10);
183
+ const salt = dict.s;
184
+ const rnonce = dict.r;
185
+
186
+ // Set up start of proof
187
+ const withoutProof = `c=biws,r=${rnonce}`;
188
+ const saltedPassword = HI(
189
+ processedPassword,
190
+ Buffer.from(salt, 'base64'),
191
+ iterations,
192
+ cryptoMethod
193
+ );
194
+
195
+ if (iterations && iterations < 4096) {
196
+ const error = new MongoError(`Server returned an invalid iteration count ${iterations}`);
197
+ return callback(error, false);
198
+ }
199
+
200
+ const clientKey = HMAC(cryptoMethod, saltedPassword, 'Client Key');
201
+ const storedKey = H(cryptoMethod, clientKey);
202
+ const authMessage = [firstBare, payload.value().toString('base64'), withoutProof].join(',');
203
+
204
+ const clientSignature = HMAC(cryptoMethod, storedKey, authMessage);
205
+ const clientProof = `p=${xor(clientKey, clientSignature)}`;
206
+ const clientFinal = [withoutProof, clientProof].join(',');
207
+ const saslContinueCmd = {
208
+ saslContinue: 1,
209
+ conversationId: r.conversationId,
210
+ payload: new Binary(Buffer.from(clientFinal))
211
+ };
212
+
213
+ sendAuthCommand(connection, `${db}.$cmd`, saslContinueCmd, (err, r) => {
214
+ if (!r || r.done !== false) {
215
+ return callback(err, r);
216
+ }
217
+
218
+ const retrySaslContinueCmd = {
219
+ saslContinue: 1,
220
+ conversationId: r.conversationId,
221
+ payload: Buffer.alloc(0)
222
+ };
223
+
224
+ sendAuthCommand(connection, `${db}.$cmd`, retrySaslContinueCmd, callback);
225
+ });
226
+ });
227
+ }
228
+
229
+ /**
230
+ * Implementation of authentication for a single connection
231
+ * @override
232
+ */
233
+ _authenticateSingleConnection(sendAuthCommand, connection, credentials, callback) {
234
+ // Create a random nonce
235
+ crypto.randomBytes(24, (err, buff) => {
236
+ if (err) {
237
+ return callback(err, null);
238
+ }
239
+
240
+ return this._executeScram(
241
+ sendAuthCommand,
242
+ connection,
243
+ credentials,
244
+ buff.toString('base64'),
245
+ callback
246
+ );
247
+ });
248
+ }
249
+
250
+ /**
251
+ * Authenticate
252
+ * @override
253
+ * @method
254
+ */
255
+ auth(sendAuthCommand, connections, credentials, callback) {
256
+ this._checkSaslprep();
257
+ super.auth(sendAuthCommand, connections, credentials, callback);
258
+ }
259
+
260
+ _checkSaslprep() {
261
+ const cryptoMethod = this.cryptoMethod;
262
+
263
+ if (cryptoMethod === 'sha256') {
264
+ if (!saslprep) {
265
+ console.warn('Warning: no saslprep library specified. Passwords will not be sanitized');
266
+ }
267
+ }
268
+ }
269
+ }
270
+
271
+ /**
272
+ * Creates a new ScramSHA1 authentication mechanism
273
+ * @class
274
+ * @extends ScramSHA
275
+ */
276
+ class ScramSHA1 extends ScramSHA {
277
+ constructor(bson) {
278
+ super(bson, 'sha1');
279
+ }
280
+ }
281
+
282
+ /**
283
+ * Creates a new ScramSHA256 authentication mechanism
284
+ * @class
285
+ * @extends ScramSHA
286
+ */
287
+ class ScramSHA256 extends ScramSHA {
288
+ constructor(bson) {
289
+ super(bson, 'sha256');
290
+ }
291
+ }
292
+
293
+ module.exports = { ScramSHA1, ScramSHA256 };
@@ -0,0 +1,131 @@
1
+ 'use strict';
2
+
3
+ const AuthProvider = require('./auth_provider').AuthProvider;
4
+ const retrieveKerberos = require('../utils').retrieveKerberos;
5
+ let kerberos;
6
+
7
+ /**
8
+ * Creates a new SSPI authentication mechanism
9
+ * @class
10
+ * @extends AuthProvider
11
+ */
12
+ class SSPI extends AuthProvider {
13
+ /**
14
+ * Implementation of authentication for a single connection
15
+ * @override
16
+ */
17
+ _authenticateSingleConnection(sendAuthCommand, connection, credentials, callback) {
18
+ // TODO: Destructure this
19
+ const username = credentials.username;
20
+ const password = credentials.password;
21
+ const mechanismProperties = credentials.mechanismProperties;
22
+ const gssapiServiceName =
23
+ mechanismProperties['gssapiservicename'] ||
24
+ mechanismProperties['gssapiServiceName'] ||
25
+ 'mongodb';
26
+
27
+ SSIPAuthenticate(
28
+ this,
29
+ kerberos.processes.MongoAuthProcess,
30
+ username,
31
+ password,
32
+ gssapiServiceName,
33
+ sendAuthCommand,
34
+ connection,
35
+ mechanismProperties,
36
+ callback
37
+ );
38
+ }
39
+
40
+ /**
41
+ * Authenticate
42
+ * @override
43
+ * @method
44
+ */
45
+ auth(sendAuthCommand, connections, credentials, callback) {
46
+ if (kerberos == null) {
47
+ try {
48
+ kerberos = retrieveKerberos();
49
+ } catch (e) {
50
+ return callback(e, null);
51
+ }
52
+ }
53
+
54
+ super.auth(sendAuthCommand, connections, credentials, callback);
55
+ }
56
+ }
57
+
58
+ function SSIPAuthenticate(
59
+ self,
60
+ MongoAuthProcess,
61
+ username,
62
+ password,
63
+ gssapiServiceName,
64
+ sendAuthCommand,
65
+ connection,
66
+ options,
67
+ callback
68
+ ) {
69
+ const authProcess = new MongoAuthProcess(
70
+ connection.host,
71
+ connection.port,
72
+ gssapiServiceName,
73
+ options
74
+ );
75
+
76
+ function authCommand(command, authCb) {
77
+ sendAuthCommand(connection, '$external.$cmd', command, authCb);
78
+ }
79
+
80
+ authProcess.init(username, password, err => {
81
+ if (err) return callback(err, false);
82
+
83
+ authProcess.transition('', (err, payload) => {
84
+ if (err) return callback(err, false);
85
+
86
+ const command = {
87
+ saslStart: 1,
88
+ mechanism: 'GSSAPI',
89
+ payload,
90
+ autoAuthorize: 1
91
+ };
92
+
93
+ authCommand(command, (err, doc) => {
94
+ if (err) return callback(err, false);
95
+
96
+ authProcess.transition(doc.payload, (err, payload) => {
97
+ if (err) return callback(err, false);
98
+ const command = {
99
+ saslContinue: 1,
100
+ conversationId: doc.conversationId,
101
+ payload
102
+ };
103
+
104
+ authCommand(command, (err, doc) => {
105
+ if (err) return callback(err, false);
106
+
107
+ authProcess.transition(doc.payload, (err, payload) => {
108
+ if (err) return callback(err, false);
109
+ const command = {
110
+ saslContinue: 1,
111
+ conversationId: doc.conversationId,
112
+ payload
113
+ };
114
+
115
+ authCommand(command, (err, response) => {
116
+ if (err) return callback(err, false);
117
+
118
+ authProcess.transition(null, err => {
119
+ if (err) return callback(err, null);
120
+ callback(null, response);
121
+ });
122
+ });
123
+ });
124
+ });
125
+ });
126
+ });
127
+ });
128
+ });
129
+ }
130
+
131
+ module.exports = SSPI;
@@ -0,0 +1,26 @@
1
+ 'use strict';
2
+
3
+ const AuthProvider = require('./auth_provider').AuthProvider;
4
+
5
+ /**
6
+ * Creates a new X509 authentication mechanism
7
+ * @class
8
+ * @extends AuthProvider
9
+ */
10
+ class X509 extends AuthProvider {
11
+ /**
12
+ * Implementation of authentication for a single connection
13
+ * @override
14
+ */
15
+ _authenticateSingleConnection(sendAuthCommand, connection, credentials, callback) {
16
+ const username = credentials.username;
17
+ const command = { authenticate: 1, mechanism: 'MONGODB-X509' };
18
+ if (username) {
19
+ command.user = username;
20
+ }
21
+
22
+ sendAuthCommand(connection, '$external.$cmd', command, callback);
23
+ }
24
+ }
25
+
26
+ module.exports = X509;
@@ -0,0 +1,236 @@
1
+ 'use strict';
2
+ const Msg = require('../connection/msg').Msg;
3
+ const KillCursor = require('../connection/commands').KillCursor;
4
+ const GetMore = require('../connection/commands').GetMore;
5
+ 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
+ ]);
19
+
20
+ // helper methods
21
+ const extractCommandName = commandDoc => Object.keys(commandDoc)[0];
22
+ const namespace = command => command.ns;
23
+ const databaseName = command => command.ns.split('.')[0];
24
+ const collectionName = command => command.ns.split('.')[1];
25
+ const generateConnectionId = pool => `${pool.options.host}:${pool.options.port}`;
26
+ const maybeRedact = (commandName, result) => (SENSITIVE_COMMANDS.has(commandName) ? {} : result);
27
+
28
+ const LEGACY_FIND_QUERY_MAP = {
29
+ $query: 'filter',
30
+ $orderby: 'sort',
31
+ $hint: 'hint',
32
+ $comment: 'comment',
33
+ $maxScan: 'maxScan',
34
+ $max: 'max',
35
+ $min: 'min',
36
+ $returnKey: 'returnKey',
37
+ $showDiskLoc: 'showRecordId',
38
+ $maxTimeMS: 'maxTimeMS',
39
+ $snapshot: 'snapshot'
40
+ };
41
+
42
+ const LEGACY_FIND_OPTIONS_MAP = {
43
+ numberToSkip: 'skip',
44
+ numberToReturn: 'batchSize',
45
+ returnFieldsSelector: 'projection'
46
+ };
47
+
48
+ const OP_QUERY_KEYS = [
49
+ 'tailable',
50
+ 'oplogReplay',
51
+ 'noCursorTimeout',
52
+ 'awaitData',
53
+ 'partial',
54
+ 'exhaust'
55
+ ];
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
+ if (command instanceof GetMore) {
65
+ return {
66
+ getMore: command.cursorId,
67
+ collection: collectionName(command),
68
+ batchSize: command.numberToReturn
69
+ };
70
+ }
71
+
72
+ if (command instanceof KillCursor) {
73
+ return {
74
+ killCursors: collectionName(command),
75
+ cursors: command.cursorIds
76
+ };
77
+ }
78
+
79
+ if (command instanceof Msg) {
80
+ return command.command;
81
+ }
82
+
83
+ if (command.query && command.query.$query) {
84
+ let result;
85
+ if (command.ns === 'admin.$cmd') {
86
+ // upconvert legacy command
87
+ result = Object.assign({}, command.query.$query);
88
+ } else {
89
+ // upconvert legacy find command
90
+ result = { find: collectionName(command) };
91
+ Object.keys(LEGACY_FIND_QUERY_MAP).forEach(key => {
92
+ if (typeof command.query[key] !== 'undefined')
93
+ result[LEGACY_FIND_QUERY_MAP[key]] = command.query[key];
94
+ });
95
+ }
96
+
97
+ Object.keys(LEGACY_FIND_OPTIONS_MAP).forEach(key => {
98
+ if (typeof command[key] !== 'undefined') result[LEGACY_FIND_OPTIONS_MAP[key]] = command[key];
99
+ });
100
+
101
+ OP_QUERY_KEYS.forEach(key => {
102
+ if (command[key]) result[key] = command[key];
103
+ });
104
+
105
+ if (typeof command.pre32Limit !== 'undefined') {
106
+ result.limit = command.pre32Limit;
107
+ }
108
+
109
+ if (command.query.$explain) {
110
+ return { explain: result };
111
+ }
112
+
113
+ return result;
114
+ }
115
+
116
+ return command.query ? command.query : command;
117
+ };
118
+
119
+ const extractReply = (command, reply) => {
120
+ if (command instanceof GetMore) {
121
+ return {
122
+ ok: 1,
123
+ cursor: {
124
+ id: reply.message.cursorId,
125
+ ns: namespace(command),
126
+ nextBatch: reply.message.documents
127
+ }
128
+ };
129
+ }
130
+
131
+ if (command instanceof KillCursor) {
132
+ return {
133
+ ok: 1,
134
+ cursorsUnknown: command.cursorIds
135
+ };
136
+ }
137
+
138
+ // is this a legacy find command?
139
+ if (command.query && typeof command.query.$query !== 'undefined') {
140
+ return {
141
+ ok: 1,
142
+ cursor: {
143
+ id: reply.message.cursorId,
144
+ ns: namespace(command),
145
+ firstBatch: reply.message.documents
146
+ }
147
+ };
148
+ }
149
+
150
+ // in the event of a `noResponse` command, just return
151
+ if (reply === null) return reply;
152
+
153
+ return reply.result;
154
+ };
155
+
156
+ /** An event indicating the start of a given command */
157
+ class CommandStartedEvent {
158
+ /**
159
+ * Create a started event
160
+ *
161
+ * @param {Pool} pool the pool that originated the command
162
+ * @param {Object} command the command
163
+ */
164
+ constructor(pool, command) {
165
+ const cmd = extractCommand(command);
166
+ const commandName = extractCommandName(cmd);
167
+
168
+ // NOTE: remove in major revision, this is not spec behavior
169
+ if (SENSITIVE_COMMANDS.has(commandName)) {
170
+ this.commandObj = {};
171
+ this.commandObj[commandName] = true;
172
+ }
173
+
174
+ Object.assign(this, {
175
+ command: cmd,
176
+ databaseName: databaseName(command),
177
+ commandName,
178
+ requestId: command.requestId,
179
+ connectionId: generateConnectionId(pool)
180
+ });
181
+ }
182
+ }
183
+
184
+ /** An event indicating the success of a given command */
185
+ class CommandSucceededEvent {
186
+ /**
187
+ * Create a succeeded event
188
+ *
189
+ * @param {Pool} pool the pool that originated the command
190
+ * @param {Object} command the command
191
+ * @param {Object} reply the reply for this command from the server
192
+ * @param {Array} started a high resolution tuple timestamp of when the command was first sent, to calculate duration
193
+ */
194
+ constructor(pool, command, reply, started) {
195
+ const cmd = extractCommand(command);
196
+ const commandName = extractCommandName(cmd);
197
+
198
+ Object.assign(this, {
199
+ duration: calculateDurationInMs(started),
200
+ commandName,
201
+ reply: maybeRedact(commandName, extractReply(command, reply)),
202
+ requestId: command.requestId,
203
+ connectionId: generateConnectionId(pool)
204
+ });
205
+ }
206
+ }
207
+
208
+ /** An event indicating the failure of a given command */
209
+ class CommandFailedEvent {
210
+ /**
211
+ * Create a failure event
212
+ *
213
+ * @param {Pool} pool the pool that originated the command
214
+ * @param {Object} command the command
215
+ * @param {MongoError|Object} error the generated error or a server error response
216
+ * @param {Array} started a high resolution tuple timestamp of when the command was first sent, to calculate duration
217
+ */
218
+ constructor(pool, command, error, started) {
219
+ const cmd = extractCommand(command);
220
+ const commandName = extractCommandName(cmd);
221
+
222
+ Object.assign(this, {
223
+ duration: calculateDurationInMs(started),
224
+ commandName,
225
+ failure: maybeRedact(commandName, error),
226
+ requestId: command.requestId,
227
+ connectionId: generateConnectionId(pool)
228
+ });
229
+ }
230
+ }
231
+
232
+ module.exports = {
233
+ CommandStartedEvent,
234
+ CommandSucceededEvent,
235
+ CommandFailedEvent
236
+ };
@@ -0,0 +1,36 @@
1
+ 'use strict';
2
+
3
+ /**
4
+ * Creates a new CommandResult instance
5
+ * @class
6
+ * @param {object} result CommandResult object
7
+ * @param {Connection} connection A connection instance associated with this result
8
+ * @return {CommandResult} A cursor instance
9
+ */
10
+ var CommandResult = function(result, connection, message) {
11
+ this.result = result;
12
+ this.connection = connection;
13
+ this.message = message;
14
+ };
15
+
16
+ /**
17
+ * Convert CommandResult to JSON
18
+ * @method
19
+ * @return {object}
20
+ */
21
+ CommandResult.prototype.toJSON = function() {
22
+ let result = Object.assign({}, this, this.result);
23
+ delete result.message;
24
+ return result;
25
+ };
26
+
27
+ /**
28
+ * Convert CommandResult to String representation
29
+ * @method
30
+ * @return {string}
31
+ */
32
+ CommandResult.prototype.toString = function() {
33
+ return JSON.stringify(this.toJSON());
34
+ };
35
+
36
+ module.exports = CommandResult;