From dc9fd615cbd8c4493dccbfe44b9192268bff79a1 Mon Sep 17 00:00:00 2001 From: Daniel Diaz <39510674+IslandRhythms@users.noreply.github.com> Date: Wed, 7 Feb 2024 17:48:35 -0500 Subject: [PATCH 1/7] begin `withSession` --- lib/connection.js | 9 +++++++++ test/connection.test.js | 6 ++++++ 2 files changed, 15 insertions(+) diff --git a/lib/connection.js b/lib/connection.js index 1eae83a037c..adc566ea957 100644 --- a/lib/connection.js +++ b/lib/connection.js @@ -443,6 +443,15 @@ Connection.prototype.createCollections = async function createCollections(option return result; }; +/** + * A wrapper for startSession() followed by session.withTransaction(fn) + */ + + Connection.prototype.withSession = async function withSession(opts, fn) { + const session = await this.startSession(opts); + return session.withTransaction(fn); + } + /** * _Requires MongoDB >= 3.6.0._ Starts a [MongoDB session](https://www.mongodb.com/docs/manual/release-notes/3.6/#client-sessions) * for benefits like causal consistency, [retryable writes](https://www.mongodb.com/docs/manual/core/retryable-writes/), diff --git a/test/connection.test.js b/test/connection.test.js index 4b5ac0493c6..823ac165417 100644 --- a/test/connection.test.js +++ b/test/connection.test.js @@ -1553,6 +1553,12 @@ describe('connections:', function() { }); assert.deepEqual(m.connections.length, 0); }); + it('should demonstrate the withSession() function (gh-14330)', async function() { + const m = new mongoose.Mongoose(); + await m.connect(/* put mongodb connection string here */); + const res = await m.connection.withSession({}, async () => { return new Promise((resolve, reject) => { return resolve('ok')})});; + assert.ok(res); + }); describe('createCollections()', function() { it('should create collections for all models on the connection with the createCollections() function (gh-13300)', async function() { const m = new mongoose.Mongoose(); From 8e1f5ef13479e5b63421e7ba4206554b77d2c06f Mon Sep 17 00:00:00 2001 From: Daniel Diaz <39510674+IslandRhythms@users.noreply.github.com> Date: Wed, 7 Feb 2024 17:49:59 -0500 Subject: [PATCH 2/7] fix: lint --- lib/connection.js | 8 ++++---- test/connection.test.js | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/lib/connection.js b/lib/connection.js index adc566ea957..482c8a390be 100644 --- a/lib/connection.js +++ b/lib/connection.js @@ -447,10 +447,10 @@ Connection.prototype.createCollections = async function createCollections(option * A wrapper for startSession() followed by session.withTransaction(fn) */ - Connection.prototype.withSession = async function withSession(opts, fn) { - const session = await this.startSession(opts); - return session.withTransaction(fn); - } +Connection.prototype.withSession = async function withSession(opts, fn) { + const session = await this.startSession(opts); + return session.withTransaction(fn); +}; /** * _Requires MongoDB >= 3.6.0._ Starts a [MongoDB session](https://www.mongodb.com/docs/manual/release-notes/3.6/#client-sessions) diff --git a/test/connection.test.js b/test/connection.test.js index 823ac165417..08c32e5de83 100644 --- a/test/connection.test.js +++ b/test/connection.test.js @@ -1556,7 +1556,7 @@ describe('connections:', function() { it('should demonstrate the withSession() function (gh-14330)', async function() { const m = new mongoose.Mongoose(); await m.connect(/* put mongodb connection string here */); - const res = await m.connection.withSession({}, async () => { return new Promise((resolve, reject) => { return resolve('ok')})});; + const res = await m.connection.withSession({}, async() => { return new Promise((resolve, reject) => { return resolve('ok');});}); assert.ok(res); }); describe('createCollections()', function() { From 9f0b13ebffc363f0738fc86c7615a08703f3bf23 Mon Sep 17 00:00:00 2001 From: Daniel Diaz <39510674+IslandRhythms@users.noreply.github.com> Date: Wed, 7 Feb 2024 17:57:21 -0500 Subject: [PATCH 3/7] Update connection.test.js --- test/connection.test.js | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/test/connection.test.js b/test/connection.test.js index 08c32e5de83..ed01f108a0e 100644 --- a/test/connection.test.js +++ b/test/connection.test.js @@ -1554,8 +1554,11 @@ describe('connections:', function() { assert.deepEqual(m.connections.length, 0); }); it('should demonstrate the withSession() function (gh-14330)', async function() { + if (!process.env.REPLICA_SET && !process.env.START_REPLICA_SET) { + this.skip(); + } const m = new mongoose.Mongoose(); - await m.connect(/* put mongodb connection string here */); + m.connect(start.uri); const res = await m.connection.withSession({}, async() => { return new Promise((resolve, reject) => { return resolve('ok');});}); assert.ok(res); }); From a1a5c5aaad62c43fba22d1f205a5bdd3c36686e0 Mon Sep 17 00:00:00 2001 From: Daniel Diaz <39510674+IslandRhythms@users.noreply.github.com> Date: Wed, 7 Feb 2024 17:58:55 -0500 Subject: [PATCH 4/7] remove unused var --- test/connection.test.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/connection.test.js b/test/connection.test.js index ed01f108a0e..0b195bad5f9 100644 --- a/test/connection.test.js +++ b/test/connection.test.js @@ -1559,7 +1559,7 @@ describe('connections:', function() { } const m = new mongoose.Mongoose(); m.connect(start.uri); - const res = await m.connection.withSession({}, async() => { return new Promise((resolve, reject) => { return resolve('ok');});}); + const res = await m.connection.withSession({}, async() => { return new Promise((resolve) => { return resolve('ok');});}); assert.ok(res); }); describe('createCollections()', function() { From 579ca48237a280b054ca7095b24cae7bbe3ff886 Mon Sep 17 00:00:00 2001 From: Daniel Diaz <39510674+IslandRhythms@users.noreply.github.com> Date: Wed, 7 Feb 2024 18:11:05 -0500 Subject: [PATCH 5/7] make opts optional --- lib/connection.js | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/lib/connection.js b/lib/connection.js index 482c8a390be..ae3ed62512b 100644 --- a/lib/connection.js +++ b/lib/connection.js @@ -448,8 +448,13 @@ Connection.prototype.createCollections = async function createCollections(option */ Connection.prototype.withSession = async function withSession(opts, fn) { - const session = await this.startSession(opts); - return session.withTransaction(fn); + if (arguments.length === 0) { + throw new Error('Please provide a callback function'); + } + const options = arguments.length === 1 ? {} : opts; + const session = await this.startSession(options); + const cb = arguments.length === 1 ? opts : fn; + return session.withTransaction(cb); }; /** From 12e1224ae4a426e7ca09340f349afb72d24d6f88 Mon Sep 17 00:00:00 2001 From: Valeri Karpov Date: Thu, 8 Feb 2024 12:26:28 -0500 Subject: [PATCH 6/7] fix: quick fixes for withSession --- lib/connection.js | 22 +++++++++++++++------- test/connection.test.js | 7 +++++-- 2 files changed, 20 insertions(+), 9 deletions(-) diff --git a/lib/connection.js b/lib/connection.js index ae3ed62512b..542bdf3c572 100644 --- a/lib/connection.js +++ b/lib/connection.js @@ -444,17 +444,25 @@ Connection.prototype.createCollections = async function createCollections(option }; /** - * A wrapper for startSession() followed by session.withTransaction(fn) + * A convenience wrapper for `connection.client.withSession()`. + * + * #### Example: + * + * await conn.withSession(async session => { + * const doc = await TestModel.findOne().session(session); + * }); + * + * @method withSession + * @param {Function} executor called with 1 argument: a `ClientSession` instance + * @return {Promise} resolves to the return value of the executor function + * @api public */ -Connection.prototype.withSession = async function withSession(opts, fn) { +Connection.prototype.withSession = async function withSession(executor) { if (arguments.length === 0) { - throw new Error('Please provide a callback function'); + throw new Error('Please provide an executor function'); } - const options = arguments.length === 1 ? {} : opts; - const session = await this.startSession(options); - const cb = arguments.length === 1 ? opts : fn; - return session.withTransaction(cb); + return await this.client.withSession(executor); }; /** diff --git a/test/connection.test.js b/test/connection.test.js index 0b195bad5f9..9ea81e356d0 100644 --- a/test/connection.test.js +++ b/test/connection.test.js @@ -1559,8 +1559,11 @@ describe('connections:', function() { } const m = new mongoose.Mongoose(); m.connect(start.uri); - const res = await m.connection.withSession({}, async() => { return new Promise((resolve) => { return resolve('ok');});}); - assert.ok(res); + let session = null; + await m.connection.withSession(s => { + session = s; + }); + assert.ok(session); }); describe('createCollections()', function() { it('should create collections for all models on the connection with the createCollections() function (gh-13300)', async function() { From ef5e09e4d4f027a4cb8528e0ca6f8e6fc4593bba Mon Sep 17 00:00:00 2001 From: Valeri Karpov Date: Thu, 8 Feb 2024 12:28:42 -0500 Subject: [PATCH 7/7] types(connection): add withSession() --- types/connection.d.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/types/connection.d.ts b/types/connection.d.ts index 35714b13c8d..b2812d01cf6 100644 --- a/types/connection.d.ts +++ b/types/connection.d.ts @@ -240,6 +240,8 @@ declare module 'mongoose' { /** Watches the entire underlying database for changes. Similar to [`Model.watch()`](/docs/api/model.html#model_Model-watch). */ watch(pipeline?: Array, options?: mongodb.ChangeStreamOptions): mongodb.ChangeStream; + + withSession(executor: (session: ClientSession) => Promise): T; } }