Skip to content

Commit 0260934

Browse files
committed
fix issue #663
1 parent fda2bb5 commit 0260934

File tree

2 files changed

+123
-7
lines changed

2 files changed

+123
-7
lines changed

lib/mongodb.js

Lines changed: 103 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -437,13 +437,11 @@ MongoDB.prototype.connect = function(callback) {
437437
if (callback) callback(err);
438438
}
439439

440-
const urlObj = new URL(self.settings.url);
441-
442-
if ((urlObj.pathname === '' ||
443-
urlObj.pathname.split('/')[1] === '') &&
444-
typeof self.settings.database === 'string') {
445-
urlObj.pathname = self.settings.database;
446-
self.settings.url = urlObj.toString();
440+
// This is special processing if database is not part of url, but is in settings
441+
if (self.settings.url && self.settings.database) {
442+
if (self.settings.url.indexOf('/' + self.settings.database) === -1) {
443+
self.settings.url = processMongoDBURL(self.settings.database, self.settings.url);
444+
}
447445
}
448446

449447
const mongoClient = new mongodb.MongoClient(self.settings.url, validOptions);
@@ -2586,3 +2584,101 @@ function buildOptions(requiredOptions, connectorOptions) {
25862584
return Object.assign({}, connectorOptions, requiredOptions);
25872585
}
25882586
}
2587+
2588+
/**
2589+
* This method parses a Mongo connection url string and refers the formats
2590+
* specified at: https://www.mongodb.com/docs/manual/reference/connection-string/.
2591+
* Since there are cases where database is not specified in the url, but as a settings property,
2592+
* the code has to reflect that in the url otherwise the MongoDB driver defaults to 'admin' db.
2593+
* @param {string} settingsDatabase - the database that will be added if url doesn't have a db specified
2594+
* @param {string} mongoUrl - the url to be processed for database manipulation
2595+
*/
2596+
function processMongoDBURL(settingsDatabase, mongoUrl) {
2597+
// Reference: https://www.mongodb.com/docs/manual/reference/connection-string/
2598+
// Standard format::: mongodb://[username:password@]host1[:port1][,...hostN[:portN]][/[defaultauthdb][?options]]
2599+
// DNS SeedList format::: mongodb+srv://server.example.com/?connectTimeoutMS=300000&authSource=aDifferentAuthDB
2600+
// Actual replicaset example::: mongodb://mongodb1.example.com:27317,mongodb2.example.com:27017/?connectTimeoutMS=300000&replicaSet=mySet&authSource=aDifferentAuthDB
2601+
2602+
if (mongoUrl) {
2603+
// 1. Know protocol
2604+
let baseUrl = '';
2605+
if (mongoUrl.startsWith('mongodb:'))
2606+
baseUrl = 'mongodb://';
2607+
else if (mongoUrl.startsWith('mongodb+srv:'))
2608+
baseUrl = 'mongodb+srv://';
2609+
else if (mongoUrl.startsWith('loopback-connector-mongodb:'))
2610+
baseUrl = 'loopback-connector-mongodb://';
2611+
else if (mongoUrl.startsWith('loopback-connector-mongodb+srv:'))
2612+
baseUrl = 'loopback-connector-mongodb+srv://';
2613+
else
2614+
return mongoUrl; // Not a MongoURL that we can process
2615+
2616+
let remainderUrl = mongoUrl.substring(baseUrl.length);
2617+
// 2. Check if userId/password is present
2618+
let uidPassword = '';
2619+
if (remainderUrl.indexOf('@') !== -1) {
2620+
const parts = remainderUrl.split('@');
2621+
uidPassword = parts[0];
2622+
if (parts.length === 2)
2623+
remainderUrl = parts[1];
2624+
else
2625+
remainderUrl = '';
2626+
}
2627+
let hosts = '';
2628+
let dbName = '';
2629+
let options = '';
2630+
let hostsArray = [];
2631+
// 3. Check if comma separated replicas are specified
2632+
if (remainderUrl.indexOf(',') !== -1) {
2633+
hostsArray = remainderUrl.split(',');
2634+
remainderUrl = hostsArray[hostsArray.length - 1];
2635+
}
2636+
2637+
// 4. Check if authDB is specified in the URL
2638+
const slashIndex = remainderUrl.indexOf('/');
2639+
if ((slashIndex !== -1)) {
2640+
if (slashIndex !== (remainderUrl.length - 1)) {
2641+
const optionsIndex = remainderUrl.indexOf('?');
2642+
if (optionsIndex !== -1) {
2643+
options = remainderUrl.substring(optionsIndex + 1);
2644+
dbName = remainderUrl.substring(slashIndex + 1, optionsIndex);
2645+
} else {
2646+
// No DB options specified
2647+
dbName = remainderUrl.substring(slashIndex + 1);
2648+
}
2649+
}
2650+
2651+
if (hostsArray.length > 1) {
2652+
const newHosts = hostsArray;
2653+
newHosts.pop();
2654+
newHosts.push(remainderUrl.substring(0, slashIndex));
2655+
hosts = newHosts.join(',');
2656+
} else {
2657+
hosts = remainderUrl.substring(0, slashIndex);
2658+
}
2659+
} else {
2660+
// No database specified
2661+
if (hostsArray.length > 1)
2662+
hosts = hostsArray.join(',');
2663+
else
2664+
hosts = remainderUrl;
2665+
}
2666+
2667+
// 5. Reconstruct url, but this time add database from settings if URL didn't have it
2668+
// The below code has an overlap with generateMongoDBURL()
2669+
let modifiedUrl = baseUrl;
2670+
2671+
if (uidPassword)
2672+
modifiedUrl += uidPassword + '@';
2673+
if (hosts)
2674+
modifiedUrl += hosts;
2675+
2676+
modifiedUrl += '/' + (dbName ? dbName : settingsDatabase);
2677+
2678+
if (options)
2679+
modifiedUrl += '?' + options;
2680+
2681+
return modifiedUrl;
2682+
}
2683+
return mongoUrl;
2684+
}

test/mongodb.test.js

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -141,6 +141,26 @@ describe('connect', function() {
141141
},
142142
);
143143
});
144+
145+
it('should connect on cluster url connection string', function(done) {
146+
// Example cluster URL (replace with a real one for actual tests)
147+
const clusterUrl = 'mongodb://user:[email protected]:27017,127.0.0.1:27017,127.0.0.1:27017/test_db?retryWrites=true&loadBalanced=false&readPreference=primary&authSource=test_db&authMechanism=SCRAM-SHA-256';
148+
const ds = global.getDataSource({
149+
url: clusterUrl,
150+
serverSelectionTimeoutMS: 2000,
151+
lazyConnect: false,
152+
});
153+
154+
ds.once('connected', function() {
155+
ds.disconnect(done);
156+
});
157+
158+
ds.on('error', function(err) {
159+
// If you don't have a real cluster, just check error is connection-related
160+
err.name.should.match(/Mongo/);
161+
done();
162+
});
163+
});
144164
});
145165

146166
describe('mongodb connector', function() {

0 commit comments

Comments
 (0)