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