diff --git a/packages/compass-collection-stats/config/webpack.dev.config.js b/packages/compass-collection-stats/config/webpack.dev.config.js index 0caba4ff8ee..d8b99f247fe 100644 --- a/packages/compass-collection-stats/config/webpack.dev.config.js +++ b/packages/compass-collection-stats/config/webpack.dev.config.js @@ -93,6 +93,9 @@ const config = { .on('close', () => process.exit(0)) .on('error', spawnError => console.error(spawnError)); // eslint-disable-line no-console } + }, + externals: { + 'mongodb-client-encryption': 'commonjs2 mongodb-client-encryption' } }; diff --git a/packages/compass-collection-stats/electron/index.js b/packages/compass-collection-stats/electron/index.js index cda2607f580..5434a5136c7 100644 --- a/packages/compass-collection-stats/electron/index.js +++ b/packages/compass-collection-stats/electron/index.js @@ -16,7 +16,12 @@ if ( process.defaultApp || /[\\/]electron-prebuilt[\\/]/.test(process.execPath) function createWindow() { // Create the browser window. mainWindow = new BrowserWindow({ - width: 1024, height: 768, show: false + width: 1024, + height: 768, + show: false, + webPreferences: { + nodeIntegration: true + } }); // and load the index.html of the app. diff --git a/packages/compass-collection-stats/src/stores/store.js b/packages/compass-collection-stats/src/stores/store.js index 7cd6ef53bfa..0ae16079724 100644 --- a/packages/compass-collection-stats/src/stores/store.js +++ b/packages/compass-collection-stats/src/stores/store.js @@ -2,6 +2,9 @@ import Reflux from 'reflux'; import StateMixin from 'reflux-state-mixin'; import toNS from 'mongodb-ns'; import numeral from 'numeral'; +import createDebug from 'debug'; +const debug = createDebug('compass-collection-stats:store'); + /** * Invalid stats. @@ -104,21 +107,58 @@ const configureStore = (options = {}) => { * @param {String} ns - The namespace. */ loadCollectionStats() { - if (toNS(this.ns || '').collection) { - if (this.state.isReadonly) { - this.setState(this.getInitialState()); - } else if (this.dataService && this.dataService.isConnected()) { - this.dataService.collection(this.ns, {}, (err, result) => { - if (!err) { - const details = this._parseCollectionDetails(result); - this.setState(details); - if (this.globalAppRegistry) { - this.globalAppRegistry.emit('compass:collection-stats:loaded', details); - } - } - }); - } + const collectionName = toNS(this.ns || '').collection; + if (!collectionName) { + return; + } + + if (this.state.isReadonly) { + this.setState(this.getInitialState()); + } else if (this.dataService && this.dataService.isConnected()) { + this.fetchCollectionDetails(); + } + }, + + handleFetchError(err) { + debug('failed to fetch collection details', err); + this.setState(this.getInitialState()); + }, + + ensureDocumentCount(result, cb) { + if (result.document_count !== undefined) { + return cb(null, result); } + + this.dataService.estimatedCount(this.ns, {}, (err, estimatedCount) => { + if (err) { + return cb(null, result); // ignore the error + } + + cb(null, { + ...result, + document_count: estimatedCount + }); + }); + }, + + fetchCollectionDetails() { + this.dataService.collection(this.ns, {}, (collectionError, result) => { + if (collectionError) { + return this.handleFetchError(collectionError); + } + + this.ensureDocumentCount(result, (ensureDocumentCountError, resultWithDocumentCount) => { + if (ensureDocumentCountError) { + return this.handleFetchError(ensureDocumentCountError); + } + + const details = this._parseCollectionDetails(resultWithDocumentCount); + this.setState(details); + if (this.globalAppRegistry) { + this.globalAppRegistry.emit('compass:collection-stats:loaded', details); + } + }); + }); }, /** @@ -148,7 +188,7 @@ const configureStore = (options = {}) => { _parseCollectionDetails(result) { return { isReadonly: this.state.isReadonly || false, - documentCount: this._format(result.document_count), + documentCount: result.document_count !== undefined ? this._format(result.document_count) : INVALID, totalDocumentSize: this._format(result.document_size, 'b'), avgDocumentSize: this._format(this._avg(result.document_size, result.document_count), 'b'), indexCount: this._format(result.index_count), diff --git a/packages/compass-collection-stats/src/stores/store.spec.js b/packages/compass-collection-stats/src/stores/store.spec.js index 48f6edb309f..c3b5987c139 100644 --- a/packages/compass-collection-stats/src/stores/store.spec.js +++ b/packages/compass-collection-stats/src/stores/store.spec.js @@ -1,5 +1,7 @@ import AppRegistry from 'hadron-app-registry'; import configureStore from 'stores'; +import sinon from 'sinon'; + describe('CollectionStatsstore [store]', () => { describe('#configureStore', () => { @@ -79,18 +81,87 @@ describe('CollectionStatsstore [store]', () => { context('when providing a data provider', () => { let store; - + let estimatedCountStub; + let collectionStub; + let dataService; beforeEach(() => { + estimatedCountStub = sinon.stub(); + collectionStub = sinon.stub(); + + dataService = { + estimatedCount: estimatedCountStub, + collection: collectionStub, + isConnected: () => true + }; + store = configureStore({ dataProvider: { error: null, - dataProvider: 'test' - } + dataProvider: dataService, + }, + namespace: 'db.coll' }); }); it('sets the data provider on the store', () => { - expect(store.dataService).to.equal('test'); + expect(store.dataService).to.equal(dataService); + }); + + it('fetches details', () => { + collectionStub.callsFake((ns, options, cb) => { + cb(null, { + document_count: 123 + }); + }); + + store.loadCollectionStats(); + + expect(store.state.documentCount).to.equal('123'); + }); + + it('re-fetch the document count if was not in stats (support hack for time-series in v5.0)', () => { + collectionStub.callsFake((ns, options, cb) => { + cb(null, {}); + }); + + estimatedCountStub.callsFake((ns, options, cb) => { + cb(null, 123); + }); + + store.loadCollectionStats(); + + expect(store.state.documentCount).to.equal('123'); + }); + + it('resets the state in case of error (collection stats)', () => { + collectionStub.callsFake((ns, options, cb) => { + cb(new Error('failed')); + }); + + store.state.documentCount = '123'; + + store.loadCollectionStats(); + + expect(store.state.documentCount).to.equal('N/A'); + expect(store.dataService).to.equal(dataService); + }); + + it('resets only the documentCount in case of error on estimated count', () => { + collectionStub.callsFake((ns, options, cb) => { + cb(null, { + index_count: 123 + }); + }); + + estimatedCountStub.callsFake((ns, options, cb) => { + cb(new Error('failed')); + }); + + store.loadCollectionStats(); + + expect(store.state.indexCount).to.equal('123'); + expect(store.state.documentCount).to.equal('N/A'); + expect(store.dataService).to.equal(dataService); }); }); diff --git a/packages/data-service/lib/native-client.js b/packages/data-service/lib/native-client.js index fdb22b9fba7..882bf78f6a2 100644 --- a/packages/data-service/lib/native-client.js +++ b/packages/data-service/lib/native-client.js @@ -428,7 +428,7 @@ class NativeClient extends EventEmitter { */ collectionStats(databaseName, collectionName, callback) { var db = this._database(databaseName); - db.command({ collStats: collectionName, verbose: 1 }, (error, data) => { + db.command({ collStats: collectionName, verbose: true }, (error, data) => { if (error && !error.message.includes(VIEW_ERROR)) { return callback(this._translateMessage(error)); }