From 442d754fac209080003a35994b37e1ff1fb055ae Mon Sep 17 00:00:00 2001 From: Valeri Karpov Date: Wed, 20 Jan 2021 12:37:29 -0500 Subject: [PATCH] WIP: upgrade to MongoDB driver 4.0 --- README.md | 2 - index.d.ts | 24 +++-- lib/aggregate.js | 27 ++---- lib/connection.js | 93 +------------------ lib/drivers/node-mongodb-native/collection.js | 2 +- lib/drivers/node-mongodb-native/connection.js | 49 ---------- lib/query.js | 24 ++--- lib/validoptions.js | 4 +- package.json | 5 +- test/common.js | 3 - test/connection.test.js | 55 +---------- test/typescript/connectSyntax.ts | 6 +- test/typescript/global.ts | 1 - 13 files changed, 46 insertions(+), 249 deletions(-) diff --git a/README.md b/README.md index b28bed6653..d9208cb963 100644 --- a/README.md +++ b/README.md @@ -71,8 +71,6 @@ Both `connect` and `createConnection` take a `mongodb://` URI, or the parameters ```js await mongoose.connect('mongodb://localhost/my_database', { - useNewUrlParser: true, - useUnifiedTopology: true, useFindAndModify: false, useCreateIndex: true }); diff --git a/index.d.ts b/index.d.ts index 36a74e37cc..2d58402d67 100644 --- a/index.d.ts +++ b/index.d.ts @@ -879,7 +879,7 @@ declare module 'mongoose' { * if true, returns the raw result from the MongoDB driver */ rawResult?: boolean; - readPreference?: mongodb.ReadPreferenceMode; + readPreference?: string | mongodb.ReadPreferenceModeId; /** * An alias for the `new` option. `returnOriginal: false` is equivalent to `new: true`. */ @@ -1207,7 +1207,7 @@ declare module 'mongoose' { */ capped?: boolean | number | { size?: number; max?: number; autoIndexId?: boolean; }; /** Sets a default collation for every query and aggregation. */ - collation?: mongodb.CollationDocument; + collation?: mongodb.CollationOptions; /** * Mongoose by default produces a collection name by passing the model name to the utils.toCollectionName * method. This method pluralizes the name. Set this option if you need a different name for your collection. @@ -1809,7 +1809,7 @@ declare module 'mongoose' { circle(path: string, area: any): this; /** Adds a collation to this op (MongoDB 3.4 and up) */ - collation(value: mongodb.CollationDocument): this; + collation(value: mongodb.CollationOptions): this; /** Specifies the `comment` option. */ comment(val: string): this; @@ -2033,7 +2033,7 @@ declare module 'mongoose' { projection(fields?: any | null): any; /** Determines the MongoDB nodes from which to read. */ - read(pref: string | mongodb.ReadPreferenceMode, tags?: any[]): this; + read(pref: string | mongodb.ReadPreferenceModeId, tags?: any[]): this; /** Sets the readConcern option for the query. */ readConcern(level: string): this; @@ -2159,6 +2159,16 @@ declare module 'mongoose' { wtimeout(ms: number): this; } + type DotAndArrayNotation = { + readonly [key: string]: AssignableType; + }; + + type ReadonlyPartial = { + readonly [key in keyof TSchema]?: TSchema[key]; + }; + + type MatchKeysAndValues = ReadonlyPartial & DotAndArrayNotation; + type _FilterQuery = { [P in keyof T]?: P extends '_id' ? [Extract] extends [never] @@ -2172,7 +2182,7 @@ declare module 'mongoose' { export type FilterQuery = _FilterQuery>; - export type UpdateQuery = mongodb.UpdateQuery> & mongodb.MatchKeysAndValues>; + export type UpdateQuery = mongodb.UpdateQuery> & MatchKeysAndValues>; type _AllowStringsForIds = { [K in keyof T]: [Extract] extends [never] ? T[K] : T[K] | string; @@ -2274,7 +2284,7 @@ declare module 'mongoose' { catch: Promise['catch']; /** Adds a collation. */ - collation(options: mongodb.CollationDocument): this; + collation(options: mongodb.CollationOptions): this; /** Appends a new $count operator to this aggregate pipeline. */ count(countName: string): this; @@ -2327,7 +2337,7 @@ declare module 'mongoose' { pipeline(): any[]; /** Sets the readPreference option for the aggregation query. */ - read(pref: string | mongodb.ReadPreferenceMode, tags?: any[]): this; + read(pref: string | mongodb.ReadPreferenceModeId, tags?: any[]): this; /** Sets the readConcern level for the aggregation query. */ readConcern(level: string): this; diff --git a/lib/aggregate.js b/lib/aggregate.js index c06e703057..7e4093f4e9 100644 --- a/lib/aggregate.js +++ b/lib/aggregate.js @@ -975,25 +975,16 @@ Aggregate.prototype.exec = function(callback) { } const options = utils.clone(this.options || {}); - collection.aggregate(this._pipeline, options, (error, cursor) => { - if (error) { - const _opts = { error: error }; - return model.hooks.execPost('aggregate', this, [null], _opts, error => { - if (error) { - return cb(error); - } - return cb(null); - }); - } - cursor.toArray((error, result) => { - const _opts = { error: error }; - model.hooks.execPost('aggregate', this, [result], _opts, (error, result) => { - if (error) { - return cb(error); - } - cb(null, result); - }); + const cursor = collection.aggregate(this._pipeline, options); + cursor.toArray((error, result) => { + const _opts = { error: error }; + model.hooks.execPost('aggregate', this, [result], _opts, (error, result) => { + if (error) { + return cb(error); + } + + cb(null, result); }); }); }); diff --git a/lib/connection.js b/lib/connection.js index 0f311e44bb..7d2f8a1583 100644 --- a/lib/connection.js +++ b/lib/connection.js @@ -20,8 +20,6 @@ const mongodb = require('mongodb'); const pkg = require('../package.json'); const utils = require('./utils'); -const parseConnectionString = require('mongodb/lib/core').parseConnectionString; - const arrayAtomicsSymbol = require('./helpers/symbols').arrayAtomicsSymbol; const sessionNewDocuments = require('./helpers/symbols').sessionNewDocuments; @@ -776,23 +774,6 @@ Connection.prototype.openUri = function(uri, options, callback) { } delete options.dbName; - if (!('promiseLibrary' in options)) { - options.promiseLibrary = PromiseProvider.get(); - } - if (!('useNewUrlParser' in options)) { - if ('useNewUrlParser' in this.base.options) { - options.useNewUrlParser = this.base.options.useNewUrlParser; - } else { - options.useNewUrlParser = false; - } - } - if (!utils.hasUserDefinedProperty(options, 'useUnifiedTopology')) { - if (utils.hasUserDefinedProperty(this.base.options, 'useUnifiedTopology')) { - options.useUnifiedTopology = this.base.options.useUnifiedTopology; - } else { - options.useUnifiedTopology = false; - } - } if (!utils.hasUserDefinedProperty(options, 'driverInfo')) { options.driverInfo = { name: 'Mongoose', @@ -800,26 +781,6 @@ Connection.prototype.openUri = function(uri, options, callback) { }; } - const parsePromise = new Promise((resolve, reject) => { - parseConnectionString(uri, options, (err, parsed) => { - if (err) { - return reject(err); - } - if (dbName) { - this.name = dbName; - } else if (parsed.defaultDatabase) { - this.name = parsed.defaultDatabase; - } else { - this.name = get(parsed, 'auth.db', null); - } - this.host = get(parsed, 'hosts.0.host', 'localhost'); - this.port = get(parsed, 'hosts.0.port', 27017); - this.user = this.user || get(parsed, 'auth.username'); - this.pass = this.pass || get(parsed, 'auth.password'); - resolve(); - }); - }); - const promise = new Promise((resolve, reject) => { const client = new mongodb.MongoClient(uri, options); _this.client = client; @@ -836,7 +797,7 @@ Connection.prototype.openUri = function(uri, options, callback) { }); const serverSelectionError = new ServerSelectionError(); - this.$initialConnection = Promise.all([promise, parsePromise]). + this.$initialConnection = promise. then(() => this). catch(err => { if (err != null && err.name === 'MongoServerSelectionError') { @@ -911,61 +872,9 @@ function _setClient(conn, client, options, dbName) { _handleReconnect(); } }); - - db.on('close', function() { - const type = get(db, 's.topology.s.description.type', ''); - if (type !== 'ReplicaSetWithPrimary') { - // Implicitly emits 'disconnected' - conn.readyState = STATES.disconnected; - } - }); } } - // Backwards compat for mongoose 4.x - db.on('reconnect', function() { - _handleReconnect(); - }); - db.s.topology.on('reconnectFailed', function() { - conn.emit('reconnectFailed'); - }); - - if (!options.useUnifiedTopology) { - db.s.topology.on('left', function(data) { - conn.emit('left', data); - }); - } - db.s.topology.on('joined', function(data) { - conn.emit('joined', data); - }); - db.s.topology.on('fullsetup', function(data) { - conn.emit('fullsetup', data); - }); - if (get(db, 's.topology.s.coreTopology.s.pool') != null) { - db.s.topology.s.coreTopology.s.pool.on('attemptReconnect', function() { - conn.emit('attemptReconnect'); - }); - } - if (!options.useUnifiedTopology || !type.startsWith('ReplicaSet')) { - db.on('close', function() { - // Implicitly emits 'disconnected' - conn.readyState = STATES.disconnected; - }); - } - - if (!options.useUnifiedTopology) { - client.on('left', function() { - if (conn.readyState === STATES.connected && - get(db, 's.topology.s.coreTopology.s.replicaSetState.topologyType') === 'ReplicaSetNoPrimary') { - conn.readyState = STATES.disconnected; - } - }); - } - - db.on('timeout', function() { - conn.emit('timeout'); - }); - delete conn.then; delete conn.catch; conn.readyState = STATES.connected; diff --git a/lib/drivers/node-mongodb-native/collection.js b/lib/drivers/node-mongodb-native/collection.js index 88eac58dd9..9f2a48a584 100644 --- a/lib/drivers/node-mongodb-native/collection.js +++ b/lib/drivers/node-mongodb-native/collection.js @@ -229,7 +229,7 @@ function iter(i) { }; } -for (const key of Object.keys(Collection.prototype)) { +for (const key of Object.getOwnPropertyNames(Collection.prototype)) { // Janky hack to work around gh-3005 until we can get rid of the mongoose // collection abstraction const descriptor = Object.getOwnPropertyDescriptor(Collection.prototype, key); diff --git a/lib/drivers/node-mongodb-native/connection.js b/lib/drivers/node-mongodb-native/connection.js index 57e7195448..48560bb6f5 100644 --- a/lib/drivers/node-mongodb-native/connection.js +++ b/lib/drivers/node-mongodb-native/connection.js @@ -92,8 +92,6 @@ NativeConnection.prototype.useDb = function(name, options) { newConn.client = _this.client; newConn.db = _this.client.db(name); newConn.onOpen(); - // setup the events appropriately - listen(newConn); } newConn.name = name; @@ -111,53 +109,6 @@ NativeConnection.prototype.useDb = function(name, options) { return newConn; }; -/*! - * Register listeners for important events and bubble appropriately. - */ - -function listen(conn) { - if (conn.db._listening) { - return; - } - conn.db._listening = true; - - conn.db.on('close', function(force) { - if (conn._closeCalled) return; - - // the driver never emits an `open` event. auto_reconnect still - // emits a `close` event but since we never get another - // `open` we can't emit close - if (conn.db.serverConfig.autoReconnect) { - conn.readyState = STATES.disconnected; - conn.emit('close'); - return; - } - conn.onClose(force); - }); - conn.db.on('error', function(err) { - conn.emit('error', err); - }); - conn.db.on('reconnect', function() { - conn.readyState = STATES.connected; - conn.emit('reconnect'); - conn.emit('reconnected'); - conn.onOpen(); - }); - conn.db.on('timeout', function(err) { - conn.emit('timeout', err); - }); - conn.db.on('open', function(err, db) { - if (STATES.disconnected === conn.readyState && db && db.databaseName) { - conn.readyState = STATES.connected; - conn.emit('reconnect'); - conn.emit('reconnected'); - } - }); - conn.db.on('parseError', function(err) { - conn.emit('parseError', err); - }); -} - /** * Closes the connection * diff --git a/lib/query.js b/lib/query.js index d7ca1f047e..7bbaa7138c 100644 --- a/lib/query.js +++ b/lib/query.js @@ -2041,20 +2041,16 @@ Query.prototype._find = wrapThunk(function(callback) { options.projection = this._fieldsForExec(); const filter = this._conditions; - this._collection.collection.find(filter, options, (err, cursor) => { - if (err) { - return cb(err); - } - if (options.explain) { - return cursor.explain(cb); - } - try { - return cursor.toArray(cb); - } catch (err) { - cb(err); - } - }); - return null; + const cursor = this._collection.collection.find(filter, options); + + if (options.explain) { + return cursor.explain(cb); + } + try { + return cursor.toArray(cb); + } catch (err) { + return cb(err); + } }); /** diff --git a/lib/validoptions.js b/lib/validoptions.js index a287a2636c..86706c66e8 100644 --- a/lib/validoptions.js +++ b/lib/validoptions.js @@ -27,9 +27,7 @@ const VALID_OPTIONS = Object.freeze([ 'toObject', 'typePojoToMixed', 'useCreateIndex', - 'useNewUrlParser', - 'usePushEach', - 'useUnifiedTopology' + 'usePushEach' ]); module.exports = VALID_OPTIONS; diff --git a/package.json b/package.json index 614c4cc209..c8dc6d0b8f 100644 --- a/package.json +++ b/package.json @@ -19,10 +19,9 @@ ], "license": "MIT", "dependencies": { - "@types/mongodb": "^3.5.27", - "bson": "^1.1.4", + "bson": "^4.2.2", "kareem": "2.3.2", - "mongodb": "3.6.3", + "mongodb": "4.0.0-beta.0", "mongoose-legacy-pluralize": "1.0.2", "mpath": "0.8.3", "mquery": "3.2.3", diff --git a/test/common.js b/test/common.js index ac646467dc..1de39edd7a 100644 --- a/test/common.js +++ b/test/common.js @@ -23,9 +23,6 @@ if (process.env.PRINT_COLLECTIONS) { // For 3.1.3 deprecations mongoose.set('useCreateIndex', true); -mongoose.set('useNewUrlParser', true); -// 3.3.x deprecations -mongoose.set('useUnifiedTopology', true); /** * Override all Collection related queries to keep count diff --git a/test/connection.test.js b/test/connection.test.js index e81192fb7e..a3f3882788 100644 --- a/test/connection.test.js +++ b/test/connection.test.js @@ -230,21 +230,9 @@ describe('connections:', function() { db.close(done); }); - it('should accept mongodb://aaron:psw@localhost:27017/fake', function(done) { - const opts = { useNewUrlParser: true, useUnifiedTopology: false }; - const db = mongoose.createConnection('mongodb://aaron:psw@localhost:27017/fake', opts, () => { - db.close(done); - }); - assert.equal(db.pass, 'psw'); - assert.equal(db.user, 'aaron'); - assert.equal(db.name, 'fake'); - assert.equal(db.host, 'localhost'); - assert.equal(db.port, 27017); - }); - it('should accept unix domain sockets', function() { const host = encodeURIComponent('/tmp/mongodb-27017.sock'); - const db = mongoose.createConnection(`mongodb://aaron:psw@${host}/fake`, { useNewUrlParser: true }); + const db = mongoose.createConnection(`mongodb://aaron:psw@${host}/fake`); db.asPromise().catch(() => {}); assert.equal(db.name, 'fake'); assert.equal(db.host, '/tmp/mongodb-27017.sock'); @@ -307,33 +295,6 @@ describe('connections:', function() { }); }); - describe('errors', function() { - it('event fires with one listener', function(done) { - this.timeout(1500); - const db = mongoose.createConnection('mongodb://bad.notadomain/fakeeee?connectTimeoutMS=100', { - useNewUrlParser: true, - useUnifiedTopology: false // Workaround re: NODE-2250 - }); - db.asPromise().catch(() => {}); - db.on('error', function() { - // this callback has no params which triggered the bug #759 - db.close(); - done(); - }); - }); - - it('should occur without hanging when password with special chars is used (gh-460)', function(done) { - const opts = { - useNewUrlParser: true, - useUnifiedTopology: false - }; - mongoose.createConnection('mongodb://aaron:ps#w@localhost/fake?connectTimeoutMS=500', opts, function(err) { - assert.ok(err); - done(); - }); - }); - }); - describe('.model()', function() { let db; @@ -898,8 +859,6 @@ describe('connections:', function() { it('throws a MongooseServerSelectionError on server selection timeout (gh-8451)', () => { const opts = { - useNewUrlParser: true, - useUnifiedTopology: true, serverSelectionTimeoutMS: 100 }; const uri = 'mongodb://baddomain:27017/test'; @@ -917,8 +876,6 @@ describe('connections:', function() { return co(function*() { const opts = { - useNewUrlParser: true, - useUnifiedTopology: true, replicaSet: process.env.REPLICA_SET }; const conn = yield mongoose.createConnection('mongodb://localhost:27017/gh8425', opts); @@ -955,10 +912,7 @@ describe('connections:', function() { it('allows setting client on a disconnected connection (gh-9164)', function() { return co(function*() { - const client = yield mongodb.MongoClient.connect('mongodb://localhost:27017/mongoose_test', { - useNewUrlParser: true, - useUnifiedTopology: true - }); + const client = yield mongodb.MongoClient.connect('mongodb://localhost:27017/mongoose_test'); const conn = mongoose.createConnection().setClient(client); assert.equal(conn.readyState, 1); @@ -973,10 +927,7 @@ describe('connections:', function() { return co(function *() { const m = new mongoose.Mongoose; - m.connect('mongodb://localhost:27017/test_gh9496', { - useNewUrlParser: true, - useUnifiedTopology: true - }); + m.connect('mongodb://localhost:27017/test_gh9496'); const conn = yield m.connection.asPromise(); assert.ok(conn instanceof m.Connection); diff --git a/test/typescript/connectSyntax.ts b/test/typescript/connectSyntax.ts index 414a747eec..7147dc9990 100644 --- a/test/typescript/connectSyntax.ts +++ b/test/typescript/connectSyntax.ts @@ -2,13 +2,11 @@ import { connect } from 'mongoose'; // Promise connect('mongodb://localhost:27017/test', { - useNewUrlParser: true, useFindAndModify: true, - useCreateIndex: true, - useUnifiedTopology: true + useCreateIndex: true }).then(mongoose => console.log(mongoose.connect)); // Callback -connect('mongodb://localhost:27017/test', { useNewUrlParser: true, useUnifiedTopology: true }, (err: Error | null) => { +connect('mongodb://localhost:27017/test', {}, (err: Error | null) => { console.log(err); }); diff --git a/test/typescript/global.ts b/test/typescript/global.ts index 8887190240..c018aac1e7 100644 --- a/test/typescript/global.ts +++ b/test/typescript/global.ts @@ -6,7 +6,6 @@ mongoose.get('useCreateIndex'); const m: mongoose.Mongoose = new mongoose.Mongoose(); -m.set('useUnifiedTopology', true); m.STATES.connected; m.connect('mongodb://localhost:27017/test').then(() => {