From d29b3d9db6824c048b75e6a74433d3638a41163f Mon Sep 17 00:00:00 2001 From: Neal Beeken Date: Thu, 15 Sep 2022 09:36:29 -0400 Subject: [PATCH] fix(NODE-4639): allow PromiseProvider to be null (#3412) --- src/bulk/common.ts | 4 ++-- src/connection_string.ts | 4 +++- src/mongo_client.ts | 12 ++++++++---- src/promise_provider.ts | 19 ++++++++++++------- src/sessions.ts | 4 ++-- src/utils.ts | 4 ++-- .../cursor_async_iterator.test.js | 2 +- .../custom_promise_library.test.js | 15 +++++++++++++++ test/types/community/client.test-d.ts | 1 - test/unit/mongo_client.test.js | 1 - 10 files changed, 45 insertions(+), 21 deletions(-) diff --git a/src/bulk/common.ts b/src/bulk/common.ts index 6361584066..9907bd4b62 100644 --- a/src/bulk/common.ts +++ b/src/bulk/common.ts @@ -1346,13 +1346,13 @@ function handleEarlyError( err?: AnyError, callback?: Callback ): Promise | void { - const Promise = PromiseProvider.get(); if (typeof callback === 'function') { callback(err); return; } - return Promise.reject(err); + const PromiseConstructor = PromiseProvider.get() ?? Promise; + return PromiseConstructor.reject(err); } function shouldForceServerObjectId(bulkOperation: BulkOperationBase): boolean { diff --git a/src/connection_string.ts b/src/connection_string.ts index 3b0bb2dd7a..e9abc845eb 100644 --- a/src/connection_string.ts +++ b/src/connection_string.ts @@ -438,7 +438,9 @@ export function parseOptions( checkTLSOptions(mongoOptions); - if (options.promiseLibrary) PromiseProvider.set(options.promiseLibrary); + if (options.promiseLibrary) { + PromiseProvider.set(options.promiseLibrary); + } const lbError = validateLoadBalancedOptions(hosts, mongoOptions, isSRV); if (lbError) { diff --git a/src/mongo_client.ts b/src/mongo_client.ts index 5f7cef89ee..321b8a63fb 100644 --- a/src/mongo_client.ts +++ b/src/mongo_client.ts @@ -593,8 +593,12 @@ export class MongoClient extends TypedEventEmitter { return mongoClient.connect(); } } catch (error) { - if (callback) return callback(error); - else return PromiseProvider.get().reject(error); + if (callback) { + return callback(error); + } else { + const PromiseConstructor = PromiseProvider.get() ?? Promise; + return PromiseConstructor.reject(error); + } } } @@ -645,9 +649,9 @@ export class MongoClient extends TypedEventEmitter { } const session = this.startSession(options); - const Promise = PromiseProvider.get(); + const PromiseConstructor = PromiseProvider.get() ?? Promise; - return Promise.resolve() + return PromiseConstructor.resolve() .then(() => withSessionCallback(session)) .then(() => { // Do not return the result of callback diff --git a/src/promise_provider.ts b/src/promise_provider.ts index 575175ff5b..eb9a28c756 100644 --- a/src/promise_provider.ts +++ b/src/promise_provider.ts @@ -4,11 +4,11 @@ import { MongoInvalidArgumentError } from './error'; const kPromise = Symbol('promise'); interface PromiseStore { - [kPromise]?: PromiseConstructor; + [kPromise]: PromiseConstructor | null; } const store: PromiseStore = { - [kPromise]: undefined + [kPromise]: null }; /** @@ -31,7 +31,14 @@ export class PromiseProvider { * Sets the promise library * @deprecated Setting a custom promise library is deprecated the next major version will use the global Promise constructor only. */ - static set(lib: PromiseConstructor): void { + static set(lib: PromiseConstructor | null): void { + // eslint-disable-next-line no-restricted-syntax + if (lib === null) { + // Check explicitly against null since `.set()` (no args) should fall through to validate + store[kPromise] = null; + return; + } + if (!PromiseProvider.validate(lib)) { // validate return; @@ -43,9 +50,7 @@ export class PromiseProvider { * Get the stored promise library, or resolves passed in * @deprecated Setting a custom promise library is deprecated the next major version will use the global Promise constructor only. */ - static get(): PromiseConstructor { - return store[kPromise] as PromiseConstructor; + static get(): PromiseConstructor | null { + return store[kPromise]; } } - -PromiseProvider.set(global.Promise); diff --git a/src/sessions.ts b/src/sessions.ts index a2f5f339b5..68eff617d4 100644 --- a/src/sessions.ts +++ b/src/sessions.ts @@ -609,14 +609,14 @@ function attemptTransaction( fn: WithTransactionCallback, options?: TransactionOptions ): Promise { - const Promise = PromiseProvider.get(); session.startTransaction(options); let promise; try { promise = fn(session); } catch (err) { - promise = Promise.reject(err); + const PromiseConstructor = PromiseProvider.get() ?? Promise; + promise = PromiseConstructor.reject(err); } if (!isPromiseLike(promise)) { diff --git a/src/utils.ts b/src/utils.ts index 05bfe78936..017b4012eb 100644 --- a/src/utils.ts +++ b/src/utils.ts @@ -448,10 +448,10 @@ export function maybePromise( callback: Callback | undefined, wrapper: (fn: Callback) => void ): Promise | void { - const Promise = PromiseProvider.get(); + const PromiseConstructor = PromiseProvider.get() ?? Promise; let result: Promise | void; if (typeof callback !== 'function') { - result = new Promise((resolve, reject) => { + result = new PromiseConstructor((resolve, reject) => { callback = (err, res) => { if (err) return reject(err); resolve(res); diff --git a/test/integration/node-specific/cursor_async_iterator.test.js b/test/integration/node-specific/cursor_async_iterator.test.js index 7618e756b7..944362bf34 100644 --- a/test/integration/node-specific/cursor_async_iterator.test.js +++ b/test/integration/node-specific/cursor_async_iterator.test.js @@ -109,7 +109,7 @@ describe('Cursor Async Iterator Tests', function () { afterEach(() => { promiseSpy.restore(); - PromiseProvider.set(Promise); + PromiseProvider.set(null); return client.close(); }); diff --git a/test/integration/node-specific/custom_promise_library.test.js b/test/integration/node-specific/custom_promise_library.test.js index 2f663459b4..3a93bb2811 100644 --- a/test/integration/node-specific/custom_promise_library.test.js +++ b/test/integration/node-specific/custom_promise_library.test.js @@ -9,8 +9,23 @@ class CustomPromise extends Promise {} CustomPromise.prototype.isCustomMongo = true; describe('Optional PromiseLibrary', function () { + beforeEach(() => { + PromiseProvider.set(null); + }); + afterEach(() => { + PromiseProvider.set(null); + }); + + it('should initially be set to null', () => { + expect(PromiseProvider.get()).to.be.null; + }); + + it('should allow passing null to .set() to clear the set promise', () => { PromiseProvider.set(Promise); + expect(PromiseProvider.get()).to.equal(Promise); + expect(() => PromiseProvider.set(null)).to.not.throw(); + expect(PromiseProvider.get()).to.be.null; }); it('should emit a deprecation warning when a promiseLibrary is set', async () => { diff --git a/test/types/community/client.test-d.ts b/test/types/community/client.test-d.ts index 952cb3560a..757d7e4c5a 100644 --- a/test/types/community/client.test-d.ts +++ b/test/types/community/client.test-d.ts @@ -35,7 +35,6 @@ const options: MongoClientOptions = { promoteBuffers: false, authMechanism: 'SCRAM-SHA-1', forceServerObjectId: false, - promiseLibrary: Promise, directConnection: false }; diff --git a/test/unit/mongo_client.test.js b/test/unit/mongo_client.test.js index f36278628a..2ff6518e04 100644 --- a/test/unit/mongo_client.test.js +++ b/test/unit/mongo_client.test.js @@ -110,7 +110,6 @@ describe('MongoOptions', function () { return 'very unique'; } }, - promiseLibrary: global.Promise, promoteBuffers: true, promoteLongs: false, promoteValues: false,