mongodb-data-service 21.12.0 → 21.15.1

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 +40 -24
  21. package/lib/data-service.js +391 -172
  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 -453
  28. package/lib/instance-detail-helper.js.map +1 -1
  29. package/lib/{legacy-connection-model.d.ts → legacy/legacy-connection-model.d.ts} +16 -7
  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 +5 -2
  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 -99
  42. package/package.json +25 -26
  43. package/lib/.esm-wrapper.mjs +0 -3
  44. package/lib/legacy-connect.d.ts +0 -7
  45. package/lib/legacy-connect.js +0 -125
  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,34 @@
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
8
  const compass_logging_1 = __importDefault(require("@mongodb-js/compass-logging"));
7
- const debug_1 = __importDefault(require("debug"));
8
9
  const events_1 = require("events");
9
10
  const lodash_1 = require("lodash");
11
+ const mongodb_connection_string_url_1 = __importDefault(require("mongodb-connection-string-url"));
10
12
  const instance_detail_helper_1 = require("./instance-detail-helper");
11
- 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");
12
17
  const { fetch: getIndexes } = require('mongodb-index-model');
13
18
  const parseNamespace = require('mongodb-ns');
14
- const debug = debug_1.default('mongodb-data-service:data-service');
15
- const { log, mongoLogId } = compass_logging_1.default('COMPASS-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
+ }
16
23
  let id = 0;
17
24
  class DataService extends events_1.EventEmitter {
18
- constructor(model) {
25
+ constructor(connectionOptions) {
19
26
  super();
20
- this.lastSeenTopology = null;
21
27
  this._isConnecting = false;
22
- this._tunnel = null;
28
+ this._lastSeenTopology = null;
23
29
  this._isWritable = false;
24
30
  this._isMongos = false;
25
- this.model = model;
26
31
  this._id = id++;
32
+ this._connectionOptions = connectionOptions;
27
33
  }
28
34
  getMongoClientConnectionOptions() {
29
35
  return this._mongoClientConnectionOptions;
@@ -31,6 +37,15 @@ class DataService extends events_1.EventEmitter {
31
37
  _logCtx() {
32
38
  return `Connection ${this._id}`;
33
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
+ }
34
49
  collection(ns, options, callback) {
35
50
  async_1.default.parallel({
36
51
  stats: this.collectionStats.bind(this, this._databaseName(ns), this._collectionName(ns)),
@@ -58,16 +73,27 @@ class DataService extends events_1.EventEmitter {
58
73
  });
59
74
  }
60
75
  collectionStats(databaseName, collectionName, callback) {
61
- 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);
62
78
  db.command({ collStats: collectionName, verbose: true }, (error, data) => {
79
+ logop(error);
63
80
  if (error && !error.message.includes('is a view, not a collection')) {
64
81
  return callback(this._translateMessage(error));
65
82
  }
66
83
  callback(null, this._buildCollectionStats(databaseName, collectionName, data || {}));
67
84
  });
68
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
+ }
69
95
  command(databaseName, comm, callback) {
70
- const db = this.mongoClient.db(databaseName);
96
+ const db = this._initializedClient.db(databaseName);
71
97
  db.command(comm, (error, result) => {
72
98
  if (error) {
73
99
  return callback(this._translateMessage(error));
@@ -81,73 +107,152 @@ class DataService extends events_1.EventEmitter {
81
107
  isMongos() {
82
108
  return this._isMongos;
83
109
  }
84
- listCollections(databaseName, filter, options, callback) {
85
- if (typeof options === 'function') {
86
- callback = options;
87
- options = {};
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;
88
120
  }
89
- const db = this.mongoClient.db(databaseName);
90
- db.listCollections(filter, options).toArray((error, data) => {
91
- if (error) {
92
- return callback(this._translateMessage(error));
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();
93
139
  }
94
- callback(null, data);
95
- });
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
+ }
96
164
  }
97
- listDatabases(callback) {
98
- this.mongoClient.db('admin').command({
99
- listDatabases: 1,
100
- }, {
101
- readPreference: this.model.readPreference,
102
- }, (error, result) => {
103
- if (error) {
104
- 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;
105
175
  }
106
- callback(null, result === null || result === void 0 ? void 0 : result.databases);
107
- });
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
+ }
108
202
  }
109
- connect(done) {
110
- debug('connecting...');
203
+ async connect() {
204
+ if (this._client) {
205
+ debug('already connected');
206
+ return;
207
+ }
111
208
  if (this._isConnecting) {
112
- setImmediate(() => {
113
- done(new Error('Connect method has been called more than once without disconnecting.'));
114
- });
209
+ debug('connect method called more than once');
115
210
  return;
116
211
  }
212
+ debug('connecting...');
117
213
  this._isConnecting = true;
118
214
  log.info(mongoLogId(1001000014), this._logCtx(), 'Connecting', {
119
- url: this.model.driverUrlWithSsh,
215
+ url: (0, redact_1.redactConnectionString)(this._connectionOptions.connectionString),
120
216
  });
121
- legacy_connect_1.default(this.model, this.setupListeners.bind(this), (err, client, tunnel, connectionOptions) => {
122
- if (err) {
123
- this._isConnecting = false;
124
- return done(this._translateMessage(err));
125
- }
126
- this._client = client;
127
- this._tunnel = tunnel;
128
- this._mongoClientConnectionOptions = connectionOptions;
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);
129
222
  const attr = {
130
223
  isWritable: this.isWritable(),
131
224
  isMongos: this.isMongos(),
132
225
  };
133
- debug('connected!', attr);
134
226
  log.info(mongoLogId(1001000015), this._logCtx(), 'Connected', attr);
135
- this._database = this._client.db(this.model.ns || 'admin');
136
- done(null, this);
137
- this.emit('readable');
138
- });
139
- return;
227
+ debug('connected!', attr);
228
+ this._client = client;
229
+ this._tunnel = tunnel;
230
+ this._mongoClientConnectionOptions = connectionOptions;
231
+ }
232
+ finally {
233
+ this._isConnecting = false;
234
+ }
140
235
  }
141
236
  estimatedCount(ns, options, callback) {
142
- 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
+ });
143
242
  }
144
243
  count(ns, filter, options, callback) {
145
- 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
+ });
146
249
  }
147
250
  createCollection(ns, options, callback) {
148
251
  const collectionName = this._collectionName(ns);
149
- 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 });
150
254
  db.createCollection(collectionName, options, (error, result) => {
255
+ logop(error);
151
256
  if (error) {
152
257
  return callback(this._translateMessage(error));
153
258
  }
@@ -155,7 +260,9 @@ class DataService extends events_1.EventEmitter {
155
260
  });
156
261
  }
157
262
  createIndex(ns, spec, options, callback) {
263
+ const logop = this._startLogOp(mongoLogId(1001000037), 'Running createIndex', { ns, spec, options });
158
264
  this._collection(ns).createIndex(spec, options, (error, result) => {
265
+ logop(error);
159
266
  if (error) {
160
267
  return callback(this._translateMessage(error));
161
268
  }
@@ -163,18 +270,17 @@ class DataService extends events_1.EventEmitter {
163
270
  });
164
271
  }
165
272
  database(name, options, callback) {
166
- async_1.default.parallel({
167
- stats: this.databaseStats.bind(this, name),
168
- collections: this.collections.bind(this, name),
169
- }, (error, db) => {
170
- if (error) {
171
- return callback(this._translateMessage(error));
172
- }
173
- 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));
174
278
  });
175
279
  }
176
280
  deleteOne(ns, filter, options, callback) {
281
+ const logop = this._startLogOp(mongoLogId(1001000038), 'Running deleteOne', { ns });
177
282
  this._collection(ns).deleteOne(filter, options, (error, result) => {
283
+ logop(error, result);
178
284
  if (error) {
179
285
  return callback(this._translateMessage(error));
180
286
  }
@@ -182,39 +288,31 @@ class DataService extends events_1.EventEmitter {
182
288
  });
183
289
  }
184
290
  deleteMany(ns, filter, options, callback) {
291
+ const logop = this._startLogOp(mongoLogId(1001000039), 'Running deleteMany', { ns });
185
292
  this._collection(ns).deleteMany(filter, options, (error, result) => {
293
+ logop(error, result);
186
294
  if (error) {
187
295
  return callback(this._translateMessage(error));
188
296
  }
189
297
  callback(null, result);
190
298
  });
191
299
  }
192
- disconnect(callback) {
193
- if (!this._client) {
194
- setImmediate(() => {
195
- callback(null);
196
- });
197
- return;
198
- }
300
+ async disconnect() {
301
+ var _a, _b;
199
302
  log.info(mongoLogId(1001000016), this._logCtx(), 'Disconnecting');
200
- this._client.close(true, (err) => {
201
- if (this._tunnel) {
202
- debug('mongo client closed. shutting down ssh tunnel');
203
- this._tunnel.close().finally(() => {
204
- this._cleanup();
205
- log.info(mongoLogId(1001000017), this._logCtx(), 'Fully closed');
206
- debug('ssh tunnel stopped');
207
- callback(err);
208
- });
209
- }
210
- else {
211
- this._cleanup();
212
- return callback(err);
213
- }
214
- });
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');
310
+ }
215
311
  }
216
312
  dropCollection(ns, callback) {
313
+ const logop = this._startLogOp(mongoLogId(1001000059), 'Running dropCollection', { ns });
217
314
  this._collection(ns).drop((error, result) => {
315
+ logop(error, result);
218
316
  if (error) {
219
317
  return callback(this._translateMessage(error));
220
318
  }
@@ -222,9 +320,11 @@ class DataService extends events_1.EventEmitter {
222
320
  });
223
321
  }
224
322
  dropDatabase(name, callback) {
225
- this.mongoClient
323
+ const logop = this._startLogOp(mongoLogId(1001000040), 'Running dropDatabase', { db: name });
324
+ this._initializedClient
226
325
  .db(this._databaseName(name))
227
326
  .dropDatabase((error, result) => {
327
+ logop(error, result);
228
328
  if (error) {
229
329
  return callback(this._translateMessage(error));
230
330
  }
@@ -232,7 +332,9 @@ class DataService extends events_1.EventEmitter {
232
332
  });
233
333
  }
234
334
  dropIndex(ns, name, callback) {
335
+ const logop = this._startLogOp(mongoLogId(1001000060), 'Running dropIndex', { ns, name });
235
336
  this._collection(ns).dropIndex(name, (error, result) => {
337
+ logop(error, result);
236
338
  if (error) {
237
339
  return callback(this._translateMessage(error));
238
340
  }
@@ -240,20 +342,28 @@ class DataService extends events_1.EventEmitter {
240
342
  });
241
343
  }
242
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
+ });
243
349
  if (typeof options === 'function') {
244
350
  callback = options;
245
351
  options = undefined;
246
352
  }
247
353
  const cursor = this._collection(ns).aggregate(pipeline, options);
248
- if (lodash_1.isFunction(callback)) {
354
+ if ((0, lodash_1.isFunction)(callback)) {
249
355
  process.nextTick(callback, null, cursor);
250
356
  return;
251
357
  }
252
358
  return cursor;
253
359
  }
254
360
  find(ns, filter, options, callback) {
361
+ const logop = this._startLogOp(mongoLogId(1001000042), 'Running find', {
362
+ ns,
363
+ });
255
364
  const cursor = this._collection(ns).find(filter, options);
256
365
  cursor.toArray((error, documents) => {
366
+ logop(error);
257
367
  if (error) {
258
368
  return callback(this._translateMessage(error));
259
369
  }
@@ -261,10 +371,13 @@ class DataService extends events_1.EventEmitter {
261
371
  });
262
372
  }
263
373
  fetch(ns, filter, options) {
374
+ const logop = this._startLogOp(mongoLogId(1001000043), 'Running raw find', { ns });
264
375
  return this._collection(ns).find(filter, options);
265
376
  }
266
377
  findOneAndReplace(ns, filter, replacement, options, callback) {
378
+ const logop = this._startLogOp(mongoLogId(1001000044), 'Running findOneAndReplace', { ns });
267
379
  this._collection(ns).findOneAndReplace(filter, replacement, options, (error, result) => {
380
+ logop(error);
268
381
  if (error) {
269
382
  return callback(this._translateMessage(error));
270
383
  }
@@ -272,7 +385,9 @@ class DataService extends events_1.EventEmitter {
272
385
  });
273
386
  }
274
387
  findOneAndUpdate(ns, filter, update, options, callback) {
388
+ const logop = this._startLogOp(mongoLogId(1001000045), 'Running findOneAndUpdate', { ns });
275
389
  this._collection(ns).findOneAndUpdate(filter, update, options, (error, result) => {
390
+ logop(error);
276
391
  if (error) {
277
392
  return callback(this._translateMessage(error));
278
393
  }
@@ -280,9 +395,11 @@ class DataService extends events_1.EventEmitter {
280
395
  });
281
396
  }
282
397
  explain(ns, filter, options, callback) {
398
+ const logop = this._startLogOp(mongoLogId(1001000046), 'Running find explain', { ns });
283
399
  this._collection(ns)
284
400
  .find(filter, options)
285
401
  .explain((error, explanation) => {
402
+ logop(error);
286
403
  if (error) {
287
404
  return callback(this._translateMessage(error));
288
405
  }
@@ -290,35 +407,34 @@ class DataService extends events_1.EventEmitter {
290
407
  });
291
408
  }
292
409
  indexes(ns, options, callback) {
293
- 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);
294
413
  if (error) {
295
414
  return callback(this._translateMessage(error));
296
415
  }
297
416
  callback(null, data);
298
417
  });
299
418
  }
300
- instance(options, callback) {
301
- instance_detail_helper_1.getInstance(this.mongoClient, this.db, ((error, instanceData) => {
302
- if (error) {
303
- return callback(this._translateMessage(error));
304
- }
419
+ async instance() {
420
+ try {
421
+ const instanceData = await (0, instance_detail_helper_1.getInstance)(this._initializedClient);
305
422
  log.info(mongoLogId(1001000024), this._logCtx(), 'Fetched instance information', {
306
423
  serverVersion: instanceData.build.version,
307
424
  genuineMongoDB: instanceData.genuineMongoDB,
308
425
  dataLake: instanceData.dataLake,
309
426
  featureCompatibilityVersion: instanceData.featureCompatibilityVersion,
310
427
  });
311
- const instance = {
312
- ...instanceData,
313
- _id: `${this.model.hostname}:${this.model.port}`,
314
- hostname: this.model.hostname,
315
- port: this.model.port,
316
- };
317
- callback(null, instance);
318
- }));
428
+ return instanceData;
429
+ }
430
+ catch (err) {
431
+ throw this._translateMessage(err);
432
+ }
319
433
  }
320
434
  insertOne(ns, doc, options, callback) {
435
+ const logop = this._startLogOp(mongoLogId(1001000048), 'Running insertOne', { ns });
321
436
  this._collection(ns).insertOne(doc, options, (error, result) => {
437
+ logop(error, { acknowledged: result === null || result === void 0 ? void 0 : result.acknowledged });
322
438
  if (error) {
323
439
  return callback(this._translateMessage(error));
324
440
  }
@@ -326,7 +442,12 @@ class DataService extends events_1.EventEmitter {
326
442
  });
327
443
  }
328
444
  insertMany(ns, docs, options, callback) {
445
+ const logop = this._startLogOp(mongoLogId(1001000049), 'Running insertOne', { ns });
329
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
+ });
330
451
  if (error) {
331
452
  return callback(this._translateMessage(error));
332
453
  }
@@ -337,13 +458,15 @@ class DataService extends events_1.EventEmitter {
337
458
  return this._collection(ns).insertMany(docs, options);
338
459
  }
339
460
  updateCollection(ns, flags, callback) {
461
+ const logop = this._startLogOp(mongoLogId(1001000050), 'Running updateCollection', { ns });
340
462
  const collectionName = this._collectionName(ns);
341
- const db = this.mongoClient.db(this._databaseName(ns));
463
+ const db = this._initializedClient.db(this._databaseName(ns));
342
464
  const command = {
343
465
  collMod: collectionName,
344
466
  ...flags,
345
467
  };
346
468
  db.command(command, (error, result) => {
469
+ logop(error, result);
347
470
  if (error) {
348
471
  return callback(this._translateMessage(error));
349
472
  }
@@ -351,7 +474,9 @@ class DataService extends events_1.EventEmitter {
351
474
  });
352
475
  }
353
476
  updateOne(ns, filter, update, options, callback) {
477
+ const logop = this._startLogOp(mongoLogId(1001000051), 'Running updateOne', { ns });
354
478
  this._collection(ns).updateOne(filter, update, options, (error, result) => {
479
+ logop(error, result);
355
480
  if (error) {
356
481
  return callback(this._translateMessage(error));
357
482
  }
@@ -359,7 +484,9 @@ class DataService extends events_1.EventEmitter {
359
484
  });
360
485
  }
361
486
  updateMany(ns, filter, update, options, callback) {
487
+ const logop = this._startLogOp(mongoLogId(1001000052), 'Running updateMany', { ns });
362
488
  this._collection(ns).updateMany(filter, update, options, (error, result) => {
489
+ logop(error, result);
363
490
  if (error) {
364
491
  return callback(this._translateMessage(error));
365
492
  }
@@ -367,14 +494,18 @@ class DataService extends events_1.EventEmitter {
367
494
  });
368
495
  }
369
496
  currentOp(includeAll, callback) {
370
- this.mongoClient
497
+ const logop = this._startLogOp(mongoLogId(1001000053), 'Running currentOp');
498
+ this._initializedClient
371
499
  .db('admin')
372
500
  .command({ currentOp: 1, $all: includeAll }, (error, result) => {
501
+ logop(error);
373
502
  if (error) {
374
- this.mongoClient
503
+ const logop = this._startLogOp(mongoLogId(1001000054), 'Searching $cmd.sys.inprog manually');
504
+ this._initializedClient
375
505
  .db('admin')
376
506
  .collection('$cmd.sys.inprog')
377
507
  .findOne({ $all: includeAll }, (error2, result2) => {
508
+ logop(error2);
378
509
  if (error2) {
379
510
  return callback(this._translateMessage(error2));
380
511
  }
@@ -386,10 +517,12 @@ class DataService extends events_1.EventEmitter {
386
517
  });
387
518
  }
388
519
  getLastSeenTopology() {
389
- return this.lastSeenTopology;
520
+ return this._lastSeenTopology;
390
521
  }
391
522
  serverstats(callback) {
392
- 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);
393
526
  if (error) {
394
527
  return callback(this._translateMessage(error));
395
528
  }
@@ -397,7 +530,9 @@ class DataService extends events_1.EventEmitter {
397
530
  });
398
531
  }
399
532
  top(callback) {
400
- 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);
401
536
  if (error) {
402
537
  return callback(this._translateMessage(error));
403
538
  }
@@ -407,9 +542,16 @@ class DataService extends events_1.EventEmitter {
407
542
  createView(name, sourceNs, pipeline, options, callback) {
408
543
  options.viewOn = this._collectionName(sourceNs);
409
544
  options.pipeline = pipeline;
410
- 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
411
552
  .db(this._databaseName(sourceNs))
412
553
  .createCollection(name, options, (error, result) => {
554
+ logop(error, result);
413
555
  if (error) {
414
556
  return callback(this._translateMessage(error));
415
557
  }
@@ -423,8 +565,15 @@ class DataService extends events_1.EventEmitter {
423
565
  collMod: name,
424
566
  ...options,
425
567
  };
426
- 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
+ });
427
575
  db.command(command, (error, result) => {
576
+ logop(error, result);
428
577
  if (error) {
429
578
  return callback(this._translateMessage(error));
430
579
  }
@@ -457,11 +606,13 @@ class DataService extends events_1.EventEmitter {
457
606
  });
458
607
  }
459
608
  startSession() {
460
- return this.mongoClient.startSession();
609
+ return this._initializedClient.startSession();
461
610
  }
462
- killSession(session) {
463
- return this.mongoClient.db('admin').command({
464
- 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],
465
616
  });
466
617
  }
467
618
  isConnected() {
@@ -472,7 +623,6 @@ class DataService extends events_1.EventEmitter {
472
623
  if (client) {
473
624
  client.on('serverDescriptionChanged', (evt) => {
474
625
  var _a;
475
- debug('serverDescriptionChanged', evt);
476
626
  log.info(mongoLogId(1001000018), this._logCtx(), 'Server description changed', {
477
627
  address: evt.address,
478
628
  error: (_a = evt.newDescription.error) !== null && _a !== void 0 ? _a : null,
@@ -482,81 +632,135 @@ class DataService extends events_1.EventEmitter {
482
632
  this.emit('serverDescriptionChanged', evt);
483
633
  });
484
634
  client.on('serverOpening', (evt) => {
485
- debug('serverOpening', evt);
486
635
  log.info(mongoLogId(1001000019), this._logCtx(), 'Server opening', {
487
636
  address: evt.address,
488
637
  });
489
638
  this.emit('serverOpening', evt);
490
639
  });
491
640
  client.on('serverClosed', (evt) => {
492
- debug('serverClosed', evt);
493
641
  log.info(mongoLogId(1001000020), this._logCtx(), 'Server closed', {
494
642
  address: evt.address,
495
643
  });
496
644
  this.emit('serverClosed', evt);
497
645
  });
498
646
  client.on('topologyOpening', (evt) => {
499
- debug('topologyOpening', evt);
500
647
  this.emit('topologyOpening', evt);
501
648
  });
502
649
  client.on('topologyClosed', (evt) => {
503
- debug('topologyClosed', evt);
504
650
  this.emit('topologyClosed', evt);
505
651
  });
506
652
  client.on('topologyDescriptionChanged', (evt) => {
507
- debug('topologyDescriptionChanged', evt);
508
- this._isWritable = this.checkIsWritable(evt);
509
- this._isMongos = this.checkIsMongos(evt);
653
+ this._isWritable = this._checkIsWritable(evt);
654
+ this._isMongos = this._checkIsMongos(evt);
510
655
  const attr = {
511
656
  isWritable: this.isWritable(),
512
657
  isMongos: this.isMongos(),
513
658
  previousType: evt.previousDescription.type,
514
659
  newType: evt.newDescription.type,
515
660
  };
516
- debug('updated to', attr);
517
661
  log.info(mongoLogId(1001000021), this._logCtx(), 'Topology description changed', attr);
518
- this.lastSeenTopology = evt.newDescription;
662
+ this._lastSeenTopology = evt.newDescription;
519
663
  this.emit('topologyDescriptionChanged', evt);
520
664
  });
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
+ };
521
671
  client.on('serverHeartbeatSucceeded', (evt) => {
522
- debug('serverHeartbeatSucceeded', evt);
523
- log.info(mongoLogId(1001000022), this._logCtx(), 'Server heartbeat succeeded', {
524
- connectionId: evt.connectionId,
525
- duration: evt.duration,
526
- });
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
+ }
527
691
  this.emit('serverHeartbeatSucceeded', evt);
528
692
  });
529
693
  client.on('serverHeartbeatFailed', (evt) => {
530
- debug('serverHeartbeatFailed', evt);
531
- log.warn(mongoLogId(1001000023), this._logCtx(), 'Server heartbeat failed', {
532
- connectionId: evt.connectionId,
533
- duration: evt.duration,
534
- failure: evt.failure.message,
535
- });
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
+ }
536
708
  this.emit('serverHeartbeatFailed', evt);
537
709
  });
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
+ });
740
+ });
538
741
  }
539
742
  }
540
- get mongoClient() {
743
+ get _initializedClient() {
541
744
  if (!this._client) {
542
745
  throw new Error('client not yet initialized');
543
746
  }
544
747
  return this._client;
545
748
  }
546
- get db() {
547
- if (!this._database) {
548
- throw new Error('database not yet initialized');
549
- }
550
- return this._database;
749
+ get _defaultDb() {
750
+ return this._initializedClient.db();
551
751
  }
552
- databaseStats(name, callback) {
553
- const db = this.mongoClient.db(name);
554
- db.command({ dbStats: 1 }, (error, data) => {
555
- if (error) {
556
- return callback(this._translateMessage(error));
557
- }
558
- callback(null, this._buildDatabaseStats(data || {}));
559
- });
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
+ }
560
764
  }
561
765
  _buildCollectionDetail(ns, data) {
562
766
  return {
@@ -568,6 +772,7 @@ class DataService extends events_1.EventEmitter {
568
772
  };
569
773
  }
570
774
  _buildCollectionStats(databaseName, collectionName, data) {
775
+ var _a, _b, _c, _d, _e, _f;
571
776
  return {
572
777
  ns: databaseName + '.' + collectionName,
573
778
  name: collectionName,
@@ -576,11 +781,13 @@ class DataService extends events_1.EventEmitter {
576
781
  max: data.max,
577
782
  is_power_of_two: data.userFlags === 1,
578
783
  index_sizes: data.indexSizes,
579
- document_count: data.count,
784
+ document_count: (_a = data.count) !== null && _a !== void 0 ? _a : 0,
580
785
  document_size: data.size,
581
- storage_size: data.storageSize,
582
- index_count: data.nindexes,
583
- 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,
584
791
  padding_factor: data.paddingFactor,
585
792
  extent_count: data.numExtents,
586
793
  extent_last_size: data.lastExtentSize,
@@ -599,30 +806,17 @@ class DataService extends events_1.EventEmitter {
599
806
  collections: db.collections,
600
807
  };
601
808
  }
602
- _buildDatabaseStats(data) {
603
- return {
604
- document_count: data.objects,
605
- document_size: data.dataSize,
606
- storage_size: data.storageSize,
607
- index_count: data.indexes,
608
- index_size: data.indexSize,
609
- extent_count: data.numExtents,
610
- file_size: data.fileSize,
611
- ns_size: data.nsSizeMB * 1024 * 1024,
612
- };
613
- }
614
809
  _collection(ns) {
615
- return this.mongoClient
810
+ return this._initializedClient
616
811
  .db(this._databaseName(ns))
617
812
  .collection(this._collectionName(ns));
618
813
  }
619
814
  _collectionNames(databaseName, callback) {
620
- this.listCollections(databaseName, {}, { nameOnly: true }, (error, collections) => {
621
- if (error) {
622
- return callback(this._translateMessage(error));
623
- }
815
+ this.listCollections(databaseName, {}, { nameOnly: true }).then((collections) => {
624
816
  const names = collections === null || collections === void 0 ? void 0 : collections.map((c) => c.name);
625
817
  callback(null, names);
818
+ }, (error) => {
819
+ callback(this._translateMessage(error));
626
820
  });
627
821
  }
628
822
  _collectionName(ns) {
@@ -631,10 +825,10 @@ class DataService extends events_1.EventEmitter {
631
825
  _databaseName(ns) {
632
826
  return parseNamespace(ns).database;
633
827
  }
634
- checkIsWritable(evt) {
828
+ _checkIsWritable(evt) {
635
829
  return [...evt.newDescription.servers.values()].some((server) => server.isWritable);
636
830
  }
637
- checkIsMongos(evt) {
831
+ _checkIsMongos(evt) {
638
832
  return evt.newDescription.type === 'Sharded';
639
833
  }
640
834
  _translateMessage(error) {
@@ -647,14 +841,39 @@ class DataService extends events_1.EventEmitter {
647
841
  return error;
648
842
  }
649
843
  _cleanup() {
844
+ if (this._client) {
845
+ this._client.removeAllListeners();
846
+ }
650
847
  this._client = undefined;
651
- this._database = undefined;
848
+ this._tunnel = undefined;
652
849
  this._mongoClientConnectionOptions = undefined;
653
- this._tunnel = null;
654
850
  this._isWritable = false;
655
851
  this._isMongos = false;
656
852
  this._isConnecting = false;
657
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
+ }
658
877
  }
659
- module.exports = DataService;
878
+ exports.default = DataService;
660
879
  //# sourceMappingURL=data-service.js.map