Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: Fix for issue#663 #666

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
111 changes: 104 additions & 7 deletions lib/mongodb.js
Original file line number Diff line number Diff line change
Expand Up @@ -332,13 +332,11 @@ MongoDB.prototype.connect = function(callback) {
if (callback) callback(err);
}

const urlObj = new URL(self.settings.url);

if ((urlObj.pathname === '' ||
urlObj.pathname.split('/')[1] === '') &&
typeof self.settings.database === 'string') {
urlObj.pathname = self.settings.database;
self.settings.url = urlObj.toString();
// This is special processing if database is not part of url, but is in settings
if (self.settings.url && self.settings.database) {
if (self.settings.url.indexOf('/' + self.settings.database) === -1) {
self.settings.url = processMongoDBURL(self.settings.database, self.settings.url);
}
}

new mongodb.MongoClient(self.settings.url, validOptions).connect(function(
Expand Down Expand Up @@ -2118,6 +2116,105 @@ MongoDB.prototype.rollback = function(tx, cb) {
});
};

exports.processMongoDBURL = processMongoDBURL;
/**
* This method parses a Mongo connection url string and refers the formats
* specified at: https://www.mongodb.com/docs/manual/reference/connection-string/.
* Since there are cases where database is not specified in the url, but as a settings property,
* the code has to reflect that in the url otherwise the MongoDB driver defaults to 'admin' db.
* @param {string} settingsDatabase - the database that will be added if url doesn't have a db specified
* @param {string} mongoUrl - the url to be processed for database manipulation
*/
function processMongoDBURL(settingsDatabase, mongoUrl) {
// Reference: https://www.mongodb.com/docs/manual/reference/connection-string/
// Standard format::: mongodb://[username:password@]host1[:port1][,...hostN[:portN]][/[defaultauthdb][?options]]
// DNS SeedList format::: mongodb+srv://server.example.com/?connectTimeoutMS=300000&authSource=aDifferentAuthDB
// Actual replicaset example::: mongodb://mongodb1.example.com:27317,mongodb2.example.com:27017/?connectTimeoutMS=300000&replicaSet=mySet&authSource=aDifferentAuthDB

if (mongoUrl) {
// 1. Know the protocol
let baseUrl = '';
if (mongoUrl.startsWith('mongodb:'))
baseUrl = 'mongodb://';
else if (mongoUrl.startsWith('mongodb+srv:'))
baseUrl = 'mongodb+srv://';
achrinza marked this conversation as resolved.
Show resolved Hide resolved
else if (mongoUrl.startsWith('loopback-connector-mongodb:'))
baseUrl = 'loopback-connector-mongodb://';
else if (mongoUrl.startsWith('loopback-connector-mongodb+srv:'))
baseUrl = 'loopback-connector-mongodb+srv://';
else
return mongoUrl; // Not a MongoURL that we can process

let remainderUrl = mongoUrl.substring(baseUrl.length);
// 2. Check if userId/password is present
let uidPassword = '';
if (remainderUrl.indexOf('@') !== -1) {
const parts = remainderUrl.split('@');
uidPassword = parts[0];
if (parts.length === 2)
remainderUrl = parts[1];
else
remainderUrl = '';
}
let hosts = '';
let dbName = '';
let options = '';
let hostsArray = [];
// 3. Check if comma separated replicas are specified
if (remainderUrl.indexOf(',') !== -1) {
hostsArray = remainderUrl.split(',');
remainderUrl = hostsArray[hostsArray.length - 1];
}

// 4. Check if authDB is specified in the URL
const slashIndex = remainderUrl.indexOf('/');
if ((slashIndex !== -1)) {
if (slashIndex !== (remainderUrl.length - 1)) {
const optionsIndex = remainderUrl.indexOf('?');
if (optionsIndex !== -1) {
options = remainderUrl.substring(optionsIndex + 1);
dbName = remainderUrl.substring(slashIndex + 1, optionsIndex);
} else {
// No DB options specified
dbName = remainderUrl.substring(slashIndex + 1);
}
}

if (hostsArray.length > 1) {
const newHosts = hostsArray;
newHosts.pop();
newHosts.push(remainderUrl.substring(0, slashIndex));
hosts = newHosts.join(',');
} else {
hosts = remainderUrl.substring(0, slashIndex);
}
} else {
// No database specified
if (hostsArray.length > 1)
hosts = hostsArray.join(',');
else
hosts = remainderUrl;
}

// 5. Reconstruct url, but this time add database from settings if URL didn't have it
// The below code has an overlap with generateMongoDBURL()
let modifiedUrl = baseUrl;

if (uidPassword)
modifiedUrl += uidPassword + '@';
if (hosts)
modifiedUrl += hosts;

modifiedUrl += '/' + (dbName ? dbName : settingsDatabase);

if (options)
modifiedUrl += '?' + options;

return modifiedUrl;
}
return mongoUrl;
}

function isInTransation(options) {
const ops = {};
if (options && options.transaction && options.transaction.isInTransation)
Expand Down
Loading