Skip to content

Commit

Permalink
Merge pull request #20 from mongodb-js/INT-962-fix-listCollections
Browse files Browse the repository at this point in the history
INT-962 fix list collections
  • Loading branch information
imlucas committed Dec 3, 2015
1 parent 3c2e4c7 commit 6d93d71
Show file tree
Hide file tree
Showing 5 changed files with 549 additions and 100 deletions.
6 changes: 3 additions & 3 deletions packages/instance-model/.travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,9 @@ before_install:
- npm config -g list -l
- npm --version
script: npm run-script ci
cache:
directories:
- node_modules
# cache:
# directories:
# - node_modules
notifications:
flowdock: e3dc17bc8a2c1b3412abe3e5747f8291
env:
Expand Down
43 changes: 28 additions & 15 deletions packages/instance-model/lib/fetch.js
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,8 @@ function getBuildInfo(done, results) {
debug('checking we can get buildInfo...');
db.admin().buildInfo(function(err, res) {
if (err) {
// buildInfo doesn't require any privileges to run, so if it fails,
// something really went wrong and we should return the error.
debug('buildInfo failed!', err);
err.command = 'buildInfo';
return done(err);
Expand Down Expand Up @@ -131,24 +133,25 @@ function parseHostInfo(resp) {
function getHostInfo(done, results) {
var db = results.db;

debug('checking we can get hostInfo...');
var spec = {
hostInfo: 1
};
var options = {};
db.admin().command(spec, options, function(err, res) {
if (err) {
if (/^not authorized/.test(err.message)) {
debug('hostInfo unavailable for this user and thats ok!');
if (isNotAuthorized(err)) {
// if the error is that the user is not authorized, silently ignore it
// and return an empty document
debug('user does not have hostInfo privilege, returning empty document {}');
done(null, {});
return;
}
// something else went wrong and we should return the error.
debug('driver error', err);
err.command = 'hostInfo';
done(err);
return;
}
debug('got hostInfo successully!');
done(null, parseHostInfo(res));
});
}
Expand Down Expand Up @@ -179,11 +182,12 @@ function listDatabases(done, results) {
db.admin().command(spec, options, function(err, res) {
if (err) {
if (isNotAuthorized(err)) {
// we caught this further up already and really should never get here!
debug('listDatabases failed. returning empty list []');
done(null, []);
return;
}

// the command failed for another reason, report the error
debug('listDatabases failed', err);
err.command = 'listDatabases';
done(err);
Expand Down Expand Up @@ -270,9 +274,7 @@ function getDatabases(done, results) {
async.parallel(_.map(dbnames, function(name) {
var result = _.partial(getDatabase, db, name);
return result;
}), function(err, res) {
done(err, res);
});
}), done);
}


Expand All @@ -284,6 +286,7 @@ function getUserInfo(done, results) {
connectionStatus: 1,
showPrivileges: true
}, function(err, res) {
// no auth required, if this fails there was a real problem
if (err) {
done(err);
}
Expand All @@ -297,6 +300,7 @@ function getUserInfo(done, results) {
usersInfo: user,
showPrivileges: true
}, function(_err, _res) {
// should always succeed for the logged-in user
if (_err) {
done(_err);
}
Expand Down Expand Up @@ -355,6 +359,15 @@ function getDatabaseCollections(db, done) {

db.listCollections(spec, options).toArray(function(err, res) {
if (err) {
if (isNotAuthorized(err)) {
// if the error is that the user is not authorized, silently ignore it
// and return an empty list
debug('not allowed to run `listCollections` command on %s, returning'
+ ' empty result [].', db.databaseName);
return done(null, []);
}
// the command failed for another reason, report the error
debug('listCollections failed', err);
err.command = 'listCollections';
return done(err);
}
Expand Down Expand Up @@ -463,10 +476,10 @@ function getInstanceDetail(db, done) {


module.exports = getInstanceDetail;

// module.exports.getCollections = getCollections;
// module.exports.getDatabaseCollections = getDatabaseCollections;
// module.exports.getDatabases = getDatabases;
// module.exports.getDatabase = getDatabase;
// module.exports.getBuildInfo = getBuildInfo;
// module.exports.getHostInfo = getHostInfo;
module.exports.getBuildInfo = getBuildInfo;
module.exports.getHostInfo = getHostInfo;
module.exports.listDatabases = listDatabases;
module.exports.getAllowedDatabases = getAllowedDatabases;
module.exports.getAllowedCollections = getAllowedCollections;
module.exports.getDatabaseCollections = getDatabaseCollections;
module.exports.listCollections = listCollections;
135 changes: 135 additions & 0 deletions packages/instance-model/test/fetch-mocked.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,135 @@
var assert = require('assert');
var fetch = require('../').fetch;
var fixtures = require('./fixtures');

// var debug = require('debug')('mongodb-instance-model:test:fetch-mocked');


describe('unit tests on fetch functions', function() {
var makeMockDB;

before(function() {
/**
* Create a mock db object that will return an error or a result on
* any of its methods. Pass in either and error or a result, but not
* both (just like regular Errbacks).
*
* @param {Error|null} err if the call should return an error, specify
* the error object here.
* @param {Any} res if the call should return a value, specify
* the value here.
* @return {Object} a db object that behaves like the mongodb
* driver
*/
makeMockDB = function(err, res) {
var db = {};
db.admin = function() {
return {
// add more db methods here as needed.

// buildInfo is a separate function on the admin object
buildInfo: function(callback) {
return callback(err, res);
},
// all other commands return the global err/res results
command: function(command, options, callback) {
return callback(err, res);
}
};
};
return db;
};
});

describe('getBuildInfo', function() {
it('should pass on any error that buildInfo returns', function(done) {
// instead of the real db handle, pass in the mocked one
var results = {
// make a db that always returns error for db.admin().buildInfo()
db: makeMockDB(new Error('some strange error'), null)
};
fetch.getBuildInfo(function(err, res) {
assert.equal(res, null);
assert.equal(err.command, 'buildInfo');
assert.equal(err.message, 'some strange error');
done();
}, results);
});
});

describe('getHostInfo', function() {
it('should ignore auth errors gracefully', function(done) {
// instead of the real db handle, pass in the mocked one
var results = {
db: makeMockDB(new Error('not authorized on fooBarDatabase to execute command '
+ '{listCollections: true, filter: {}, cursor: {}'), null)
};
fetch.getHostInfo(function(err, res) {
assert.equal(err, null);
assert.deepEqual(res, []);
done();
}, results);
});
it('should pass on other errors from the hostInfo command', function(done) {
// instead of the real db handle, pass in the mocked one
var results = {
db: makeMockDB(new Error('some other error from hostInfo'), null)
};
fetch.getHostInfo(function(err, res) {
assert.ok(err);
assert.equal(err.command, 'hostInfo');
assert.deepEqual(res, null);
done();
}, results);
});
});

describe('listDatabases', function() {
var results = {};

beforeEach(function() {
results.userInfo = fixtures.USER_INFO;
});

it('should ignore auth errors gracefully', function(done) {
// instead of the real db handle, pass in the mocked one
results.db = makeMockDB(new Error('not authorized on admin to execute command '
+ '{ listDatabases: 1.0 }'), null);

fetch.listDatabases(function(err, res) {
assert.equal(err, null);
assert.deepEqual(res, []);
done();
}, results);
});
it('should pass on other errors from the listDatabases command', function(done) {
// instead of the real db handle, pass in the mocked one
results.db = makeMockDB(new Error('some other error from hostInfo'), null);

fetch.listDatabases(function(err, res) {
assert.ok(err);
assert.equal(err.command, 'listDatabases');
assert.deepEqual(res, null);
done();
}, results);
});
});

describe('getAllowedDatabases', function() {
it('should return the correct results');
// ...
});
describe('getAllowedCollections', function() {
it('should return the correct results');
// ...
});
describe('getDatabaseCollections', function() {
it('should ignore auth errors gracefully');
it('should pass on other errors from the listCollections command');
// ...
});
describe('listCollections', function() {
it('should merge the two collection lists correctly');
// ...
});
});
82 changes: 0 additions & 82 deletions packages/instance-model/test/fetch.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -28,50 +28,6 @@ describe('mongodb-instance-model#fetch', function() {
done();
});
});
// it('should list collections', function(done) {
// assert(db);
// fetch.getAllCollections(db, function(err, res) {
// if (err) {
// return done(err);
// }
// debug('list collections', JSON.stringify(res, null, 2));
// done();
// });
// });
//
// it('should list databases', function(done) {
// assert(db);
// fetch.getDatabases(db, function(err, res) {
// if (err) {
// return done(err);
// }
// debug('list databases', JSON.stringify(res, null, 2));
// done();
// });
// });
//
// it('should get build info', function(done) {
// assert(db);
// fetch.getBuildInfo(db, function(err, res) {
// if (err) {
// return done(err);
// }
// debug('build info', JSON.stringify(res, null, 2));
// done();
// });
// });
//
// it('should get host info', function(done) {
// assert(db);
// fetch.getHostInfo(db, function(err, res) {
// if (err) {
// return done(err);
// }
// debug('host info', JSON.stringify(res, null, 2));
// done();
// });
// });

it('should get instance details', function(done) {
assert(db);
fetch(db, function(err, res) {
Expand Down Expand Up @@ -141,44 +97,6 @@ describe('mongodb-instance-model#fetch', function() {
done();
});
});

// it('should list databases', function(done) {
// if (process.env.dry ) {
// this.skip();
// return;
// }
// this.slow(5000);
// this.timeout(10000);
// assert(db, 'requires successful connection');
//
// fetch.getDatabases(db, function(err, res) {
// if (err) return done(err);
//
// assert(Array.isArray(res));
// assert(res.length > 0, 'Database list is empty');
// done();
// });
// });
//
// it('should list collections', function(done) {
// if (process.env.dry ) {
// this.skip();
// return;
// }
// this.slow(5000);
// this.timeout(10000);
// assert(db, 'requires successful connection');
//
// fetch.getAllCollections(db, function(err, res) {
// if (err) return done(err);
//
// assert(Array.isArray(res));
// assert(res.length > 0, 'Collection list is empty');
// done();
// });
// });


it('should get instance details', function(done) {
if (process.env.dry) {
this.skip();
Expand Down
Loading

0 comments on commit 6d93d71

Please sign in to comment.