diff --git a/lib/connection.js b/lib/connection.js index 61d325a9dd2..5b3d12af85f 100644 --- a/lib/connection.js +++ b/lib/connection.js @@ -550,8 +550,8 @@ Connection.prototype.dropDatabase = _wrapConnHelper(function dropDatabase(cb) { // init-ed. It is sufficiently common to call `dropDatabase()` after // `mongoose.connect()` but before creating models that we want to // support this. See gh-6796 - for (const name of Object.keys(this.models)) { - delete this.models[name].$init; + for (const model of Object.values(this.models)) { + delete model.$init; } this.db.dropDatabase(cb); }); @@ -838,6 +838,11 @@ Connection.prototype.openUri = function(uri, options, callback) { ); } + for (const model of Object.values(this.models)) { + // Errors handled internally, so safe to ignore error + model.init(function $modelInitNoop() {}); + } + return this.$initialConnection; }; @@ -951,6 +956,13 @@ Connection.prototype.close = function(force, callback) { this.$wasForceClosed = !!force; } + for (const model of Object.values(this.models)) { + // If manually disconnecting, make sure to clear each model's `$init` + // promise, so Mongoose knows to re-run `init()` in case the + // connection is re-opened. See gh-12047. + delete model.$init; + } + return promiseOrCallback(callback, cb => { this._close(force, false, cb); }); @@ -1443,6 +1455,11 @@ Connection.prototype.setClient = function setClient(client) { this._connectionString = client.s.url; _setClient(this, client, {}, client.s.options.dbName); + for (const model of Object.values(this.models)) { + // Errors handled internally, so safe to ignore error + model.init(function $modelInitNoop() {}); + } + return this; }; diff --git a/test/connection.test.js b/test/connection.test.js index 332fb8e05af..befcbcef1d6 100644 --- a/test/connection.test.js +++ b/test/connection.test.js @@ -1135,6 +1135,72 @@ describe('connections:', function() { assert.deepStrictEqual(conn3.id, m.connection.id + 2); }); + describe('Automatic init', function() { + it('re-runs init() if connecting after disconnecting (gh-12047)', async function() { + const conn = await mongoose.createConnection(start.uri).asPromise(); + + const Test = conn.model('Test', new Schema({ name: { type: String, index: true } })); + await Test.init(); + + let indexes = await Test.collection.listIndexes().toArray(); + assert.equal(indexes.length, 2); + assert.equal(indexes[1].name, 'name_1'); + + await conn.db.dropCollection(Test.collection.collectionName); + + const err = await Test.collection.listIndexes().toArray().then(() => null, err => err); + assert.ok(err); + assert.equal(err.codeName, 'NamespaceNotFound'); + + await conn.close(); + + await conn.openUri(start.uri); + assert.ok(Test.$init); + await Test.init(); + + indexes = await Test.collection.listIndexes().toArray(); + assert.equal(indexes.length, 2); + assert.equal(indexes[1].name, 'name_1'); + }); + + it('re-runs init() if running setClient() after disconnecting (gh-12047)', async function() { + const conn = await mongoose.createConnection(start.uri).asPromise(); + + const Test = conn.model('Test', new Schema({ name: { type: String, index: true } })); + await Test.init(); + + let indexes = await Test.collection.listIndexes().toArray(); + assert.equal(indexes.length, 2); + assert.equal(indexes[1].name, 'name_1'); + + await conn.db.dropCollection(Test.collection.collectionName); + + const err = await Test.collection.listIndexes().toArray().then(() => null, err => err); + assert.ok(err); + assert.equal(err.codeName, 'NamespaceNotFound'); + + await conn.close(); + + const client = await mongodb.MongoClient.connect(start.uri); + + try { + conn.setClient(client); + assert.ok(Test.$init); + await Test.init(); + + indexes = await Test.collection.listIndexes().toArray(); + assert.equal(indexes.length, 2); + assert.equal(indexes[1].name, 'name_1'); + + await client.close(); + } catch (err) { + await client.close().catch(() => {}); + + throw err; + } + }); + }); + describe('Connection#syncIndexes() (gh-10893) (gh-11039)', () => { let connection; this.beforeEach(async() => {