mongodb-data-service 21.11.0 → 21.15.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 (48) hide show
  1. package/README.md +7 -22
  2. package/lib/connect-mongo-client.d.ts +11 -0
  3. package/lib/connect-mongo-client.js +89 -0
  4. package/lib/connect-mongo-client.js.map +1 -0
  5. package/lib/connect.js +2 -15
  6. package/lib/connect.js.map +1 -1
  7. package/lib/connection-info.d.ts +12 -0
  8. package/lib/connection-info.js +3 -0
  9. package/lib/connection-info.js.map +1 -0
  10. package/lib/connection-options.d.ts +2 -11
  11. package/lib/connection-secrets.d.ts +13 -0
  12. package/lib/connection-secrets.js +98 -0
  13. package/lib/connection-secrets.js.map +1 -0
  14. package/lib/connection-storage.d.ts +4 -6
  15. package/lib/connection-storage.js +8 -8
  16. package/lib/connection-storage.js.map +1 -1
  17. package/lib/connection-title.d.ts +2 -0
  18. package/lib/connection-title.js +25 -0
  19. package/lib/connection-title.js.map +1 -0
  20. package/lib/data-service.d.ts +42 -21
  21. package/lib/data-service.js +440 -175
  22. package/lib/data-service.js.map +1 -1
  23. package/lib/index.d.ts +8 -1
  24. package/lib/index.js +15 -1
  25. package/lib/index.js.map +1 -1
  26. package/lib/instance-detail-helper.d.ts +77 -32
  27. package/lib/instance-detail-helper.js +143 -433
  28. package/lib/instance-detail-helper.js.map +1 -1
  29. package/lib/{legacy-connection-model.d.ts → legacy/legacy-connection-model.d.ts} +17 -8
  30. package/lib/legacy/legacy-connection-model.js +207 -0
  31. package/lib/legacy/legacy-connection-model.js.map +1 -0
  32. package/lib/redact.d.ts +4 -1
  33. package/lib/redact.js +23 -11
  34. package/lib/redact.js.map +1 -1
  35. package/lib/run-command.d.ts +209 -0
  36. package/lib/run-command.js +11 -0
  37. package/lib/run-command.js.map +1 -0
  38. package/lib/ssh-tunnel.d.ts +5 -0
  39. package/lib/ssh-tunnel.js +73 -0
  40. package/lib/ssh-tunnel.js.map +1 -0
  41. package/lib/types.d.ts +7 -98
  42. package/package.json +24 -25
  43. package/lib/.esm-wrapper.mjs +0 -3
  44. package/lib/legacy-connect.d.ts +0 -7
  45. package/lib/legacy-connect.js +0 -123
  46. package/lib/legacy-connect.js.map +0 -1
  47. package/lib/legacy-connection-model.js +0 -158
  48. package/lib/legacy-connection-model.js.map +0 -1
@@ -2,28 +2,50 @@
2
2
  var __importDefault = (this && this.__importDefault) || function (mod) {
3
3
  return (mod && mod.__esModule) ? mod : { "default": mod };
4
4
  };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ const util_1 = require("util");
5
7
  const async_1 = __importDefault(require("async"));
6
- const debug_1 = __importDefault(require("debug"));
8
+ const compass_logging_1 = __importDefault(require("@mongodb-js/compass-logging"));
7
9
  const events_1 = require("events");
8
10
  const lodash_1 = require("lodash");
11
+ const mongodb_connection_string_url_1 = __importDefault(require("mongodb-connection-string-url"));
9
12
  const instance_detail_helper_1 = require("./instance-detail-helper");
10
- const legacy_connect_1 = __importDefault(require("./legacy-connect"));
13
+ const redact_1 = require("./redact");
14
+ const connect_mongo_client_1 = __importDefault(require("./connect-mongo-client"));
15
+ const get_port_1 = __importDefault(require("get-port"));
16
+ const run_command_1 = require("./run-command");
11
17
  const { fetch: getIndexes } = require('mongodb-index-model');
12
18
  const parseNamespace = require('mongodb-ns');
13
- const debug = debug_1.default('mongodb-data-service:data-service');
19
+ const { log, mongoLogId, debug } = (0, compass_logging_1.default)('COMPASS-DATA-SERVICE');
20
+ function uniqueBy(values, key) {
21
+ return Array.from(new Map(values.map((val) => [val[key], val])).values());
22
+ }
23
+ let id = 0;
14
24
  class DataService extends events_1.EventEmitter {
15
- constructor(model) {
25
+ constructor(connectionOptions) {
16
26
  super();
17
- this.lastSeenTopology = null;
18
27
  this._isConnecting = false;
19
- this._tunnel = null;
28
+ this._lastSeenTopology = null;
20
29
  this._isWritable = false;
21
30
  this._isMongos = false;
22
- this.model = model;
31
+ this._id = id++;
32
+ this._connectionOptions = connectionOptions;
23
33
  }
24
34
  getMongoClientConnectionOptions() {
25
35
  return this._mongoClientConnectionOptions;
26
36
  }
37
+ _logCtx() {
38
+ return `Connection ${this._id}`;
39
+ }
40
+ getConnectionOptions() {
41
+ return this._connectionOptions;
42
+ }
43
+ getConnectionString() {
44
+ return new mongodb_connection_string_url_1.default(this._connectionOptions.connectionString);
45
+ }
46
+ getReadPreference() {
47
+ return this._initializedClient.readPreference;
48
+ }
27
49
  collection(ns, options, callback) {
28
50
  async_1.default.parallel({
29
51
  stats: this.collectionStats.bind(this, this._databaseName(ns), this._collectionName(ns)),
@@ -51,16 +73,27 @@ class DataService extends events_1.EventEmitter {
51
73
  });
52
74
  }
53
75
  collectionStats(databaseName, collectionName, callback) {
54
- const db = this.mongoClient.db(databaseName);
76
+ const logop = this._startLogOp(mongoLogId(1001000031), 'Fetching collection info', { ns: `${databaseName}.${collectionName}` });
77
+ const db = this._initializedClient.db(databaseName);
55
78
  db.command({ collStats: collectionName, verbose: true }, (error, data) => {
79
+ logop(error);
56
80
  if (error && !error.message.includes('is a view, not a collection')) {
57
81
  return callback(this._translateMessage(error));
58
82
  }
59
83
  callback(null, this._buildCollectionStats(databaseName, collectionName, data || {}));
60
84
  });
61
85
  }
86
+ async collectionInfo(dbName, collName) {
87
+ try {
88
+ const [collInfo] = await this.listCollections(dbName, { name: collName });
89
+ return collInfo !== null && collInfo !== void 0 ? collInfo : null;
90
+ }
91
+ catch (err) {
92
+ throw this._translateMessage(err);
93
+ }
94
+ }
62
95
  command(databaseName, comm, callback) {
63
- const db = this.mongoClient.db(databaseName);
96
+ const db = this._initializedClient.db(databaseName);
64
97
  db.command(comm, (error, result) => {
65
98
  if (error) {
66
99
  return callback(this._translateMessage(error));
@@ -74,64 +107,152 @@ class DataService extends events_1.EventEmitter {
74
107
  isMongos() {
75
108
  return this._isMongos;
76
109
  }
77
- listCollections(databaseName, filter, callback) {
78
- const db = this.mongoClient.db(databaseName);
79
- db.listCollections(filter, {}).toArray((error, data) => {
80
- if (error) {
81
- return callback(this._translateMessage(error));
110
+ async connectionStatus() {
111
+ const logop = this._startLogOp(mongoLogId(1001000100), 'Running connectionStatus');
112
+ try {
113
+ const adminDb = this._initializedClient.db('admin');
114
+ const result = await (0, run_command_1.runCommand)(adminDb, {
115
+ connectionStatus: 1,
116
+ showPrivileges: true,
117
+ });
118
+ logop(null);
119
+ return result;
120
+ }
121
+ catch (e) {
122
+ logop(e);
123
+ throw e;
124
+ }
125
+ }
126
+ async _getPrivilegesOrFallback(privileges = null) {
127
+ if (privileges) {
128
+ return privileges;
129
+ }
130
+ const { authInfo: { authenticatedUserPrivileges }, } = await this.connectionStatus();
131
+ return authenticatedUserPrivileges;
132
+ }
133
+ async listCollections(databaseName, filter = {}, { nameOnly, privileges = null, } = {}) {
134
+ const logop = this._startLogOp(mongoLogId(1001000032), 'Running listCollections', { db: databaseName, nameOnly: nameOnly !== null && nameOnly !== void 0 ? nameOnly : false });
135
+ const db = this._initializedClient.db(databaseName);
136
+ const listCollections = async () => {
137
+ try {
138
+ return await db.listCollections(filter, { nameOnly }).toArray();
82
139
  }
83
- callback(null, data);
84
- });
140
+ catch (err) {
141
+ log.warn(mongoLogId(1001000099), this._logCtx(), 'Failed to run listCollections', { message: err.message });
142
+ return [];
143
+ }
144
+ };
145
+ const getCollectionsFromPrivileges = async () => {
146
+ const databases = (0, instance_detail_helper_1.getPrivilegesByDatabaseAndCollection)(await this._getPrivilegesOrFallback(privileges), ['find']);
147
+ return Object.keys(databases[databaseName] || {})
148
+ .filter(Boolean)
149
+ .map((name) => ({ name }));
150
+ };
151
+ try {
152
+ const [listedCollections, collectionsFromPrivileges] = await Promise.all([
153
+ listCollections(),
154
+ getCollectionsFromPrivileges(),
155
+ ]);
156
+ const collections = uniqueBy([...collectionsFromPrivileges, ...listedCollections], 'name').map((coll) => (0, instance_detail_helper_1.adaptCollectionInfo)({ db: databaseName, ...coll }));
157
+ logop(null, { collectionsCount: collections.length });
158
+ return collections;
159
+ }
160
+ catch (err) {
161
+ logop(err);
162
+ throw err;
163
+ }
85
164
  }
86
- listDatabases(callback) {
87
- this.mongoClient.db('admin').command({
88
- listDatabases: 1,
89
- }, {
90
- readPreference: this.model.readPreference,
91
- }, (error, result) => {
92
- if (error) {
93
- return callback(this._translateMessage(error));
165
+ async listDatabases({ nameOnly, privileges = null, } = {}) {
166
+ const logop = this._startLogOp(mongoLogId(1001000033), 'Running listDatabases', { nameOnly: nameOnly !== null && nameOnly !== void 0 ? nameOnly : false });
167
+ const adminDb = this._initializedClient.db('admin');
168
+ const listDatabases = async () => {
169
+ try {
170
+ const { databases } = await (0, run_command_1.runCommand)(adminDb, {
171
+ listDatabases: 1,
172
+ nameOnly,
173
+ });
174
+ return databases;
94
175
  }
95
- callback(null, result === null || result === void 0 ? void 0 : result.databases);
96
- });
176
+ catch (err) {
177
+ log.warn(mongoLogId(1001000098), this._logCtx(), 'Failed to run listDatabases', { message: err.message });
178
+ return [];
179
+ }
180
+ };
181
+ const getDatabasesFromPrivileges = async () => {
182
+ const databases = (0, instance_detail_helper_1.getPrivilegesByDatabaseAndCollection)(await this._getPrivilegesOrFallback(privileges), ['find']);
183
+ return Object.keys(databases)
184
+ .filter(Boolean)
185
+ .map((name) => ({ name }));
186
+ };
187
+ try {
188
+ const [listedDatabases, databasesFromPrivileges] = await Promise.all([
189
+ listDatabases(),
190
+ getDatabasesFromPrivileges(),
191
+ ]);
192
+ const databases = uniqueBy([...databasesFromPrivileges, ...listedDatabases], 'name').map((db) => {
193
+ return { _id: db.name, name: db.name, ...(0, instance_detail_helper_1.adaptDatabaseInfo)(db) };
194
+ });
195
+ logop(null, { databasesCount: databases.length });
196
+ return databases;
197
+ }
198
+ catch (err) {
199
+ logop(err);
200
+ throw err;
201
+ }
97
202
  }
98
- connect(done) {
99
- debug('connecting...');
203
+ async connect() {
204
+ if (this._client) {
205
+ debug('already connected');
206
+ return;
207
+ }
100
208
  if (this._isConnecting) {
101
- setImmediate(() => {
102
- done(new Error('Connect method has been called more than once without disconnecting.'));
103
- });
209
+ debug('connect method called more than once');
104
210
  return;
105
211
  }
212
+ debug('connecting...');
106
213
  this._isConnecting = true;
107
- legacy_connect_1.default(this.model, this.setupListeners.bind(this), (err, client, tunnel, connectionOptions) => {
108
- if (err) {
109
- this._isConnecting = false;
110
- return done(this._translateMessage(err));
111
- }
214
+ log.info(mongoLogId(1001000014), this._logCtx(), 'Connecting', {
215
+ url: (0, redact_1.redactConnectionString)(this._connectionOptions.connectionString),
216
+ });
217
+ try {
218
+ const tunnelLocalPort = this._connectionOptions.sshTunnel
219
+ ? await (0, get_port_1.default)()
220
+ : 0;
221
+ const [client, tunnel, connectionOptions] = await (0, connect_mongo_client_1.default)(this._connectionOptions, this.setupListeners.bind(this), tunnelLocalPort);
222
+ const attr = {
223
+ isWritable: this.isWritable(),
224
+ isMongos: this.isMongos(),
225
+ };
226
+ log.info(mongoLogId(1001000015), this._logCtx(), 'Connected', attr);
227
+ debug('connected!', attr);
112
228
  this._client = client;
113
229
  this._tunnel = tunnel;
114
230
  this._mongoClientConnectionOptions = connectionOptions;
115
- debug('connected!', {
116
- isWritable: this.isWritable(),
117
- isMongos: this.isMongos(),
118
- });
119
- this._database = this._client.db(this.model.ns || 'admin');
120
- done(null, this);
121
- this.emit('readable');
122
- });
123
- return;
231
+ }
232
+ finally {
233
+ this._isConnecting = false;
234
+ }
124
235
  }
125
236
  estimatedCount(ns, options, callback) {
126
- this._collection(ns).estimatedDocumentCount(options, (err, result) => callback(err, result));
237
+ const logop = this._startLogOp(mongoLogId(1001000034), 'Running estimatedCount', { ns });
238
+ this._collection(ns).estimatedDocumentCount(options, (err, result) => {
239
+ logop(err, result);
240
+ callback(err, result);
241
+ });
127
242
  }
128
243
  count(ns, filter, options, callback) {
129
- this._collection(ns).countDocuments(filter, options, (err, result) => callback(err, result));
244
+ const logop = this._startLogOp(mongoLogId(1001000035), 'Running countDocuments', { ns });
245
+ this._collection(ns).countDocuments(filter, options, (err, result) => {
246
+ logop(err, result);
247
+ callback(err, result);
248
+ });
130
249
  }
131
250
  createCollection(ns, options, callback) {
132
251
  const collectionName = this._collectionName(ns);
133
- const db = this.mongoClient.db(this._databaseName(ns));
252
+ const db = this._initializedClient.db(this._databaseName(ns));
253
+ const logop = this._startLogOp(mongoLogId(1001000036), 'Running createCollection', { ns, options });
134
254
  db.createCollection(collectionName, options, (error, result) => {
255
+ logop(error);
135
256
  if (error) {
136
257
  return callback(this._translateMessage(error));
137
258
  }
@@ -139,7 +260,9 @@ class DataService extends events_1.EventEmitter {
139
260
  });
140
261
  }
141
262
  createIndex(ns, spec, options, callback) {
263
+ const logop = this._startLogOp(mongoLogId(1001000037), 'Running createIndex', { ns, spec, options });
142
264
  this._collection(ns).createIndex(spec, options, (error, result) => {
265
+ logop(error);
143
266
  if (error) {
144
267
  return callback(this._translateMessage(error));
145
268
  }
@@ -147,18 +270,17 @@ class DataService extends events_1.EventEmitter {
147
270
  });
148
271
  }
149
272
  database(name, options, callback) {
150
- async_1.default.parallel({
151
- stats: this.databaseStats.bind(this, name),
152
- collections: this.collections.bind(this, name),
153
- }, (error, db) => {
154
- if (error) {
155
- return callback(this._translateMessage(error));
156
- }
157
- callback(null, this._buildDatabaseDetail(name, db));
273
+ const asyncColls = (0, util_1.promisify)(this.collections.bind(this));
274
+ void Promise.all([this.databaseStats(name), asyncColls(name)]).then(([stats, collections]) => {
275
+ callback(null, this._buildDatabaseDetail(name, { stats, collections }));
276
+ }, (err) => {
277
+ callback(this._translateMessage(err));
158
278
  });
159
279
  }
160
280
  deleteOne(ns, filter, options, callback) {
281
+ const logop = this._startLogOp(mongoLogId(1001000038), 'Running deleteOne', { ns });
161
282
  this._collection(ns).deleteOne(filter, options, (error, result) => {
283
+ logop(error, result);
162
284
  if (error) {
163
285
  return callback(this._translateMessage(error));
164
286
  }
@@ -166,37 +288,31 @@ class DataService extends events_1.EventEmitter {
166
288
  });
167
289
  }
168
290
  deleteMany(ns, filter, options, callback) {
291
+ const logop = this._startLogOp(mongoLogId(1001000039), 'Running deleteMany', { ns });
169
292
  this._collection(ns).deleteMany(filter, options, (error, result) => {
293
+ logop(error, result);
170
294
  if (error) {
171
295
  return callback(this._translateMessage(error));
172
296
  }
173
297
  callback(null, result);
174
298
  });
175
299
  }
176
- disconnect(callback) {
177
- if (!this._client) {
178
- setImmediate(() => {
179
- callback(null);
180
- });
181
- return;
300
+ async disconnect() {
301
+ var _a, _b;
302
+ log.info(mongoLogId(1001000016), this._logCtx(), 'Disconnecting');
303
+ try {
304
+ await ((_a = this._client) === null || _a === void 0 ? void 0 : _a.close(true).catch((err) => debug('failed to close MongoClient', err)));
305
+ await ((_b = this._tunnel) === null || _b === void 0 ? void 0 : _b.close().catch((err) => debug('failed to close tunnel', err)));
306
+ }
307
+ finally {
308
+ this._cleanup();
309
+ log.info(mongoLogId(1001000017), this._logCtx(), 'Fully closed');
182
310
  }
183
- this._client.close(true, (err) => {
184
- if (this._tunnel) {
185
- debug('mongo client closed. shutting down ssh tunnel');
186
- this._tunnel.close().finally(() => {
187
- this._cleanup();
188
- debug('ssh tunnel stopped');
189
- callback(err);
190
- });
191
- }
192
- else {
193
- this._cleanup();
194
- return callback(err);
195
- }
196
- });
197
311
  }
198
312
  dropCollection(ns, callback) {
313
+ const logop = this._startLogOp(mongoLogId(1001000059), 'Running dropCollection', { ns });
199
314
  this._collection(ns).drop((error, result) => {
315
+ logop(error, result);
200
316
  if (error) {
201
317
  return callback(this._translateMessage(error));
202
318
  }
@@ -204,9 +320,11 @@ class DataService extends events_1.EventEmitter {
204
320
  });
205
321
  }
206
322
  dropDatabase(name, callback) {
207
- this.mongoClient
323
+ const logop = this._startLogOp(mongoLogId(1001000040), 'Running dropDatabase', { db: name });
324
+ this._initializedClient
208
325
  .db(this._databaseName(name))
209
326
  .dropDatabase((error, result) => {
327
+ logop(error, result);
210
328
  if (error) {
211
329
  return callback(this._translateMessage(error));
212
330
  }
@@ -214,7 +332,9 @@ class DataService extends events_1.EventEmitter {
214
332
  });
215
333
  }
216
334
  dropIndex(ns, name, callback) {
335
+ const logop = this._startLogOp(mongoLogId(1001000060), 'Running dropIndex', { ns, name });
217
336
  this._collection(ns).dropIndex(name, (error, result) => {
337
+ logop(error, result);
218
338
  if (error) {
219
339
  return callback(this._translateMessage(error));
220
340
  }
@@ -222,20 +342,28 @@ class DataService extends events_1.EventEmitter {
222
342
  });
223
343
  }
224
344
  aggregate(ns, pipeline, options, callback) {
345
+ log.info(mongoLogId(1001000041), this._logCtx(), 'Running aggregation', {
346
+ ns,
347
+ stages: pipeline.map((stage) => Object.keys(stage)[0]),
348
+ });
225
349
  if (typeof options === 'function') {
226
350
  callback = options;
227
351
  options = undefined;
228
352
  }
229
353
  const cursor = this._collection(ns).aggregate(pipeline, options);
230
- if (lodash_1.isFunction(callback)) {
354
+ if ((0, lodash_1.isFunction)(callback)) {
231
355
  process.nextTick(callback, null, cursor);
232
356
  return;
233
357
  }
234
358
  return cursor;
235
359
  }
236
360
  find(ns, filter, options, callback) {
361
+ const logop = this._startLogOp(mongoLogId(1001000042), 'Running find', {
362
+ ns,
363
+ });
237
364
  const cursor = this._collection(ns).find(filter, options);
238
365
  cursor.toArray((error, documents) => {
366
+ logop(error);
239
367
  if (error) {
240
368
  return callback(this._translateMessage(error));
241
369
  }
@@ -243,10 +371,13 @@ class DataService extends events_1.EventEmitter {
243
371
  });
244
372
  }
245
373
  fetch(ns, filter, options) {
374
+ const logop = this._startLogOp(mongoLogId(1001000043), 'Running raw find', { ns });
246
375
  return this._collection(ns).find(filter, options);
247
376
  }
248
377
  findOneAndReplace(ns, filter, replacement, options, callback) {
378
+ const logop = this._startLogOp(mongoLogId(1001000044), 'Running findOneAndReplace', { ns });
249
379
  this._collection(ns).findOneAndReplace(filter, replacement, options, (error, result) => {
380
+ logop(error);
250
381
  if (error) {
251
382
  return callback(this._translateMessage(error));
252
383
  }
@@ -254,7 +385,9 @@ class DataService extends events_1.EventEmitter {
254
385
  });
255
386
  }
256
387
  findOneAndUpdate(ns, filter, update, options, callback) {
388
+ const logop = this._startLogOp(mongoLogId(1001000045), 'Running findOneAndUpdate', { ns });
257
389
  this._collection(ns).findOneAndUpdate(filter, update, options, (error, result) => {
390
+ logop(error);
258
391
  if (error) {
259
392
  return callback(this._translateMessage(error));
260
393
  }
@@ -262,9 +395,11 @@ class DataService extends events_1.EventEmitter {
262
395
  });
263
396
  }
264
397
  explain(ns, filter, options, callback) {
398
+ const logop = this._startLogOp(mongoLogId(1001000046), 'Running find explain', { ns });
265
399
  this._collection(ns)
266
400
  .find(filter, options)
267
401
  .explain((error, explanation) => {
402
+ logop(error);
268
403
  if (error) {
269
404
  return callback(this._translateMessage(error));
270
405
  }
@@ -272,29 +407,34 @@ class DataService extends events_1.EventEmitter {
272
407
  });
273
408
  }
274
409
  indexes(ns, options, callback) {
275
- getIndexes(this.mongoClient, ns, (error, data) => {
410
+ const logop = this._startLogOp(mongoLogId(1001000047), 'Listing indexes', { ns });
411
+ getIndexes(this._initializedClient, ns, (error, data) => {
412
+ logop(error);
276
413
  if (error) {
277
414
  return callback(this._translateMessage(error));
278
415
  }
279
416
  callback(null, data);
280
417
  });
281
418
  }
282
- instance(options, callback) {
283
- instance_detail_helper_1.getInstance(this.mongoClient, this.db, ((error, instanceData) => {
284
- if (error) {
285
- return callback(this._translateMessage(error));
286
- }
287
- const instance = {
288
- ...instanceData,
289
- _id: `${this.model.hostname}:${this.model.port}`,
290
- hostname: this.model.hostname,
291
- port: this.model.port,
292
- };
293
- callback(null, instance);
294
- }));
419
+ async instance() {
420
+ try {
421
+ const instanceData = await (0, instance_detail_helper_1.getInstance)(this._initializedClient);
422
+ log.info(mongoLogId(1001000024), this._logCtx(), 'Fetched instance information', {
423
+ serverVersion: instanceData.build.version,
424
+ genuineMongoDB: instanceData.genuineMongoDB,
425
+ dataLake: instanceData.dataLake,
426
+ featureCompatibilityVersion: instanceData.featureCompatibilityVersion,
427
+ });
428
+ return instanceData;
429
+ }
430
+ catch (err) {
431
+ throw this._translateMessage(err);
432
+ }
295
433
  }
296
434
  insertOne(ns, doc, options, callback) {
435
+ const logop = this._startLogOp(mongoLogId(1001000048), 'Running insertOne', { ns });
297
436
  this._collection(ns).insertOne(doc, options, (error, result) => {
437
+ logop(error, { acknowledged: result === null || result === void 0 ? void 0 : result.acknowledged });
298
438
  if (error) {
299
439
  return callback(this._translateMessage(error));
300
440
  }
@@ -302,7 +442,12 @@ class DataService extends events_1.EventEmitter {
302
442
  });
303
443
  }
304
444
  insertMany(ns, docs, options, callback) {
445
+ const logop = this._startLogOp(mongoLogId(1001000049), 'Running insertOne', { ns });
305
446
  this._collection(ns).insertMany(docs, options, (error, result) => {
447
+ logop(error, {
448
+ acknowledged: result === null || result === void 0 ? void 0 : result.acknowledged,
449
+ insertedCount: result === null || result === void 0 ? void 0 : result.insertedCount,
450
+ });
306
451
  if (error) {
307
452
  return callback(this._translateMessage(error));
308
453
  }
@@ -313,13 +458,15 @@ class DataService extends events_1.EventEmitter {
313
458
  return this._collection(ns).insertMany(docs, options);
314
459
  }
315
460
  updateCollection(ns, flags, callback) {
461
+ const logop = this._startLogOp(mongoLogId(1001000050), 'Running updateCollection', { ns });
316
462
  const collectionName = this._collectionName(ns);
317
- const db = this.mongoClient.db(this._databaseName(ns));
463
+ const db = this._initializedClient.db(this._databaseName(ns));
318
464
  const command = {
319
465
  collMod: collectionName,
320
466
  ...flags,
321
467
  };
322
468
  db.command(command, (error, result) => {
469
+ logop(error, result);
323
470
  if (error) {
324
471
  return callback(this._translateMessage(error));
325
472
  }
@@ -327,7 +474,9 @@ class DataService extends events_1.EventEmitter {
327
474
  });
328
475
  }
329
476
  updateOne(ns, filter, update, options, callback) {
477
+ const logop = this._startLogOp(mongoLogId(1001000051), 'Running updateOne', { ns });
330
478
  this._collection(ns).updateOne(filter, update, options, (error, result) => {
479
+ logop(error, result);
331
480
  if (error) {
332
481
  return callback(this._translateMessage(error));
333
482
  }
@@ -335,7 +484,9 @@ class DataService extends events_1.EventEmitter {
335
484
  });
336
485
  }
337
486
  updateMany(ns, filter, update, options, callback) {
487
+ const logop = this._startLogOp(mongoLogId(1001000052), 'Running updateMany', { ns });
338
488
  this._collection(ns).updateMany(filter, update, options, (error, result) => {
489
+ logop(error, result);
339
490
  if (error) {
340
491
  return callback(this._translateMessage(error));
341
492
  }
@@ -343,14 +494,18 @@ class DataService extends events_1.EventEmitter {
343
494
  });
344
495
  }
345
496
  currentOp(includeAll, callback) {
346
- this.mongoClient
497
+ const logop = this._startLogOp(mongoLogId(1001000053), 'Running currentOp');
498
+ this._initializedClient
347
499
  .db('admin')
348
500
  .command({ currentOp: 1, $all: includeAll }, (error, result) => {
501
+ logop(error);
349
502
  if (error) {
350
- this.mongoClient
503
+ const logop = this._startLogOp(mongoLogId(1001000054), 'Searching $cmd.sys.inprog manually');
504
+ this._initializedClient
351
505
  .db('admin')
352
506
  .collection('$cmd.sys.inprog')
353
507
  .findOne({ $all: includeAll }, (error2, result2) => {
508
+ logop(error2);
354
509
  if (error2) {
355
510
  return callback(this._translateMessage(error2));
356
511
  }
@@ -362,10 +517,12 @@ class DataService extends events_1.EventEmitter {
362
517
  });
363
518
  }
364
519
  getLastSeenTopology() {
365
- return this.lastSeenTopology;
520
+ return this._lastSeenTopology;
366
521
  }
367
522
  serverstats(callback) {
368
- this.db.admin().serverStatus((error, result) => {
523
+ const logop = this._startLogOp(mongoLogId(1001000061), 'Running serverStats');
524
+ this._defaultDb.admin().serverStatus((error, result) => {
525
+ logop(error);
369
526
  if (error) {
370
527
  return callback(this._translateMessage(error));
371
528
  }
@@ -373,7 +530,9 @@ class DataService extends events_1.EventEmitter {
373
530
  });
374
531
  }
375
532
  top(callback) {
376
- this.db.admin().command({ top: 1 }, (error, result) => {
533
+ const logop = this._startLogOp(mongoLogId(1001000062), 'Running top');
534
+ this._defaultDb.admin().command({ top: 1 }, (error, result) => {
535
+ logop(error);
377
536
  if (error) {
378
537
  return callback(this._translateMessage(error));
379
538
  }
@@ -383,9 +542,16 @@ class DataService extends events_1.EventEmitter {
383
542
  createView(name, sourceNs, pipeline, options, callback) {
384
543
  options.viewOn = this._collectionName(sourceNs);
385
544
  options.pipeline = pipeline;
386
- this.mongoClient
545
+ const logop = this._startLogOp(mongoLogId(1001000055), 'Running createView', {
546
+ name,
547
+ sourceNs,
548
+ stages: pipeline.map((stage) => Object.keys(stage)[0]),
549
+ options,
550
+ });
551
+ this._initializedClient
387
552
  .db(this._databaseName(sourceNs))
388
553
  .createCollection(name, options, (error, result) => {
554
+ logop(error, result);
389
555
  if (error) {
390
556
  return callback(this._translateMessage(error));
391
557
  }
@@ -399,8 +565,15 @@ class DataService extends events_1.EventEmitter {
399
565
  collMod: name,
400
566
  ...options,
401
567
  };
402
- const db = this.mongoClient.db(this._databaseName(sourceNs));
568
+ const db = this._initializedClient.db(this._databaseName(sourceNs));
569
+ const logop = this._startLogOp(mongoLogId(1001000056), 'Running updateView', {
570
+ name,
571
+ sourceNs,
572
+ stages: pipeline.map((stage) => Object.keys(stage)[0]),
573
+ options,
574
+ });
403
575
  db.command(command, (error, result) => {
576
+ logop(error, result);
404
577
  if (error) {
405
578
  return callback(this._translateMessage(error));
406
579
  }
@@ -433,11 +606,13 @@ class DataService extends events_1.EventEmitter {
433
606
  });
434
607
  }
435
608
  startSession() {
436
- return this.mongoClient.startSession();
609
+ return this._initializedClient.startSession();
437
610
  }
438
- killSession(session) {
439
- return this.mongoClient.db('admin').command({
440
- killSessions: [session.id],
611
+ killSessions(sessions) {
612
+ return this._initializedClient.db('admin').command({
613
+ killSessions: Array.isArray(sessions)
614
+ ? sessions.map((s) => s.id)
615
+ : [sessions.id],
441
616
  });
442
617
  }
443
618
  isConnected() {
@@ -446,71 +621,146 @@ class DataService extends events_1.EventEmitter {
446
621
  setupListeners(client) {
447
622
  this._client = client;
448
623
  if (client) {
449
- client.on('status', (...args) => {
450
- debug('status', args);
451
- this.emit('status', args[0]);
452
- });
453
624
  client.on('serverDescriptionChanged', (evt) => {
454
- debug('serverDescriptionChanged', evt);
625
+ var _a;
626
+ log.info(mongoLogId(1001000018), this._logCtx(), 'Server description changed', {
627
+ address: evt.address,
628
+ error: (_a = evt.newDescription.error) !== null && _a !== void 0 ? _a : null,
629
+ previousType: evt.previousDescription.type,
630
+ newType: evt.newDescription.type,
631
+ });
455
632
  this.emit('serverDescriptionChanged', evt);
456
633
  });
457
- client.on('serverOpening', (...args) => {
458
- debug('serverOpening', args);
459
- this.emit('serverOpening', args[0]);
634
+ client.on('serverOpening', (evt) => {
635
+ log.info(mongoLogId(1001000019), this._logCtx(), 'Server opening', {
636
+ address: evt.address,
637
+ });
638
+ this.emit('serverOpening', evt);
460
639
  });
461
- client.on('serverClosed', (...args) => {
462
- debug('serverClosed', args);
463
- this.emit('serverClosed', args[0]);
640
+ client.on('serverClosed', (evt) => {
641
+ log.info(mongoLogId(1001000020), this._logCtx(), 'Server closed', {
642
+ address: evt.address,
643
+ });
644
+ this.emit('serverClosed', evt);
464
645
  });
465
- client.on('topologyOpening', (...args) => {
466
- debug('topologyOpening', args);
467
- this.emit('topologyOpening', args[0]);
646
+ client.on('topologyOpening', (evt) => {
647
+ this.emit('topologyOpening', evt);
468
648
  });
469
- client.on('topologyClosed', (...args) => {
470
- debug('topologyClosed', args);
471
- this.emit('topologyClosed', args[0]);
649
+ client.on('topologyClosed', (evt) => {
650
+ this.emit('topologyClosed', evt);
472
651
  });
473
- client.on('topologyDescriptionChanged', (...args) => {
474
- debug('topologyDescriptionChanged', args);
475
- this._isWritable = this.checkIsWritable(args[0]);
476
- this._isMongos = this.checkIsMongos(args[0]);
477
- debug('updated to', {
652
+ client.on('topologyDescriptionChanged', (evt) => {
653
+ this._isWritable = this._checkIsWritable(evt);
654
+ this._isMongos = this._checkIsMongos(evt);
655
+ const attr = {
478
656
  isWritable: this.isWritable(),
479
657
  isMongos: this.isMongos(),
480
- });
481
- this.lastSeenTopology = args[0].newDescription;
482
- this.emit('topologyDescriptionChanged', args[0]);
658
+ previousType: evt.previousDescription.type,
659
+ newType: evt.newDescription.type,
660
+ };
661
+ log.info(mongoLogId(1001000021), this._logCtx(), 'Topology description changed', attr);
662
+ this._lastSeenTopology = evt.newDescription;
663
+ this.emit('topologyDescriptionChanged', evt);
483
664
  });
484
- client.on('serverHeartbeatSucceeded', (...args) => {
485
- debug('serverHeartbeatSucceeded', args);
486
- this.emit('serverHeartbeatSucceeded', args[0]);
665
+ const heartbeatStatusMap = new Map();
666
+ const isSignificantDurationChange = (before, after) => {
667
+ before = Math.max(before, 200);
668
+ after = Math.max(after, 200);
669
+ return Math.abs(Math.log2(before / after)) >= 1;
670
+ };
671
+ client.on('serverHeartbeatSucceeded', (evt) => {
672
+ const previousStatus = heartbeatStatusMap.get(evt.connectionId);
673
+ if (!previousStatus ||
674
+ previousStatus.failure ||
675
+ isSignificantDurationChange(previousStatus.duration, evt.duration)) {
676
+ heartbeatStatusMap.set(evt.connectionId, {
677
+ failure: null,
678
+ duration: evt.duration,
679
+ });
680
+ log.write({
681
+ s: 'D2',
682
+ id: mongoLogId(1001000022),
683
+ ctx: this._logCtx(),
684
+ msg: 'Server heartbeat succeeded',
685
+ attr: {
686
+ connectionId: evt.connectionId,
687
+ duration: evt.duration,
688
+ },
689
+ });
690
+ }
691
+ this.emit('serverHeartbeatSucceeded', evt);
692
+ });
693
+ client.on('serverHeartbeatFailed', (evt) => {
694
+ const previousStatus = heartbeatStatusMap.get(evt.connectionId);
695
+ if (!previousStatus ||
696
+ !previousStatus.failure ||
697
+ isSignificantDurationChange(previousStatus.duration, evt.duration)) {
698
+ heartbeatStatusMap.set(evt.connectionId, {
699
+ failure: evt.failure.message,
700
+ duration: evt.duration,
701
+ });
702
+ log.warn(mongoLogId(1001000023), this._logCtx(), 'Server heartbeat failed', {
703
+ connectionId: evt.connectionId,
704
+ duration: evt.duration,
705
+ failure: evt.failure.message,
706
+ });
707
+ }
708
+ this.emit('serverHeartbeatFailed', evt);
487
709
  });
488
- client.on('serverHeartbeatFailed', (...args) => {
489
- debug('serverHeartbeatFailed', args);
490
- this.emit('serverHeartbeatFailed', args[0]);
710
+ client.on('commandSucceeded', (evt) => {
711
+ const { address, connectionId, duration, commandName } = evt;
712
+ log.write({
713
+ s: 'D2',
714
+ id: mongoLogId(1001000029),
715
+ ctx: this._logCtx(),
716
+ msg: 'Driver command succeeded',
717
+ attr: {
718
+ address,
719
+ serverConnectionId: connectionId,
720
+ duration,
721
+ commandName,
722
+ },
723
+ });
724
+ });
725
+ client.on('commandFailed', (evt) => {
726
+ const { address, connectionId, duration, commandName, failure } = evt;
727
+ log.write({
728
+ s: 'D1',
729
+ id: mongoLogId(1001000030),
730
+ ctx: this._logCtx(),
731
+ msg: 'Driver command failed',
732
+ attr: {
733
+ address,
734
+ serverConnectionId: connectionId,
735
+ duration,
736
+ commandName,
737
+ failure: failure.message,
738
+ },
739
+ });
491
740
  });
492
741
  }
493
742
  }
494
- get mongoClient() {
743
+ get _initializedClient() {
495
744
  if (!this._client) {
496
745
  throw new Error('client not yet initialized');
497
746
  }
498
747
  return this._client;
499
748
  }
500
- get db() {
501
- if (!this._database) {
502
- throw new Error('database not yet initialized');
503
- }
504
- return this._database;
749
+ get _defaultDb() {
750
+ return this._initializedClient.db();
505
751
  }
506
- databaseStats(name, callback) {
507
- const db = this.mongoClient.db(name);
508
- db.command({ dbStats: 1 }, (error, data) => {
509
- if (error) {
510
- return callback(this._translateMessage(error));
511
- }
512
- callback(null, this._buildDatabaseStats(data || {}));
513
- });
752
+ async databaseStats(name) {
753
+ const logop = this._startLogOp(mongoLogId(1001000057), 'Running databaseStats', { db: name });
754
+ try {
755
+ const db = this._initializedClient.db(name);
756
+ const stats = await (0, run_command_1.runCommand)(db, { dbStats: 1 });
757
+ const normalized = (0, instance_detail_helper_1.adaptDatabaseInfo)(stats);
758
+ return { name, ...normalized };
759
+ }
760
+ catch (err) {
761
+ logop(err);
762
+ throw this._translateMessage(err);
763
+ }
514
764
  }
515
765
  _buildCollectionDetail(ns, data) {
516
766
  return {
@@ -522,6 +772,7 @@ class DataService extends events_1.EventEmitter {
522
772
  };
523
773
  }
524
774
  _buildCollectionStats(databaseName, collectionName, data) {
775
+ var _a, _b, _c, _d, _e, _f;
525
776
  return {
526
777
  ns: databaseName + '.' + collectionName,
527
778
  name: collectionName,
@@ -530,11 +781,13 @@ class DataService extends events_1.EventEmitter {
530
781
  max: data.max,
531
782
  is_power_of_two: data.userFlags === 1,
532
783
  index_sizes: data.indexSizes,
533
- document_count: data.count,
784
+ document_count: (_a = data.count) !== null && _a !== void 0 ? _a : 0,
534
785
  document_size: data.size,
535
- storage_size: data.storageSize,
536
- index_count: data.nindexes,
537
- index_size: data.totalIndexSize,
786
+ avg_document_size: (_b = data.avgObjSize) !== null && _b !== void 0 ? _b : 0,
787
+ storage_size: (_c = data.storageSize) !== null && _c !== void 0 ? _c : 0,
788
+ free_storage_size: (_d = data.freeStorageSize) !== null && _d !== void 0 ? _d : 0,
789
+ index_count: (_e = data.nindexes) !== null && _e !== void 0 ? _e : 0,
790
+ index_size: (_f = data.totalIndexSize) !== null && _f !== void 0 ? _f : 0,
538
791
  padding_factor: data.paddingFactor,
539
792
  extent_count: data.numExtents,
540
793
  extent_last_size: data.lastExtentSize,
@@ -553,30 +806,17 @@ class DataService extends events_1.EventEmitter {
553
806
  collections: db.collections,
554
807
  };
555
808
  }
556
- _buildDatabaseStats(data) {
557
- return {
558
- document_count: data.objects,
559
- document_size: data.dataSize,
560
- storage_size: data.storageSize,
561
- index_count: data.indexes,
562
- index_size: data.indexSize,
563
- extent_count: data.numExtents,
564
- file_size: data.fileSize,
565
- ns_size: data.nsSizeMB * 1024 * 1024,
566
- };
567
- }
568
809
  _collection(ns) {
569
- return this.mongoClient
810
+ return this._initializedClient
570
811
  .db(this._databaseName(ns))
571
812
  .collection(this._collectionName(ns));
572
813
  }
573
814
  _collectionNames(databaseName, callback) {
574
- this.listCollections(databaseName, {}, (error, collections) => {
575
- if (error) {
576
- return callback(this._translateMessage(error));
577
- }
815
+ this.listCollections(databaseName, {}, { nameOnly: true }).then((collections) => {
578
816
  const names = collections === null || collections === void 0 ? void 0 : collections.map((c) => c.name);
579
817
  callback(null, names);
818
+ }, (error) => {
819
+ callback(this._translateMessage(error));
580
820
  });
581
821
  }
582
822
  _collectionName(ns) {
@@ -585,10 +825,10 @@ class DataService extends events_1.EventEmitter {
585
825
  _databaseName(ns) {
586
826
  return parseNamespace(ns).database;
587
827
  }
588
- checkIsWritable(evt) {
828
+ _checkIsWritable(evt) {
589
829
  return [...evt.newDescription.servers.values()].some((server) => server.isWritable);
590
830
  }
591
- checkIsMongos(evt) {
831
+ _checkIsMongos(evt) {
592
832
  return evt.newDescription.type === 'Sharded';
593
833
  }
594
834
  _translateMessage(error) {
@@ -601,14 +841,39 @@ class DataService extends events_1.EventEmitter {
601
841
  return error;
602
842
  }
603
843
  _cleanup() {
844
+ if (this._client) {
845
+ this._client.removeAllListeners();
846
+ }
604
847
  this._client = undefined;
605
- this._database = undefined;
848
+ this._tunnel = undefined;
606
849
  this._mongoClientConnectionOptions = undefined;
607
- this._tunnel = null;
608
850
  this._isWritable = false;
609
851
  this._isMongos = false;
610
852
  this._isConnecting = false;
611
853
  }
854
+ _startLogOp(logId, op, attr = {}) {
855
+ return (error, result) => {
856
+ if (error) {
857
+ const { message } = this._translateMessage(error);
858
+ log.error(mongoLogId(1001000058), this._logCtx(), 'Failed to perform data service operation', {
859
+ op,
860
+ message,
861
+ ...attr,
862
+ });
863
+ }
864
+ else {
865
+ if (result) {
866
+ attr = { ...attr, result };
867
+ }
868
+ if (Object.keys(attr).length > 0) {
869
+ log.info(logId, this._logCtx(), op, attr);
870
+ }
871
+ else {
872
+ log.info(logId, this._logCtx(), op);
873
+ }
874
+ }
875
+ };
876
+ }
612
877
  }
613
- module.exports = DataService;
878
+ exports.default = DataService;
614
879
  //# sourceMappingURL=data-service.js.map