Skip to content

Commit e5b762c

Browse files
author
Thomas Reggi
authored
feat: implements promise provider
Adds `PromiseProvider` storage class to save the user-provided promise library. Eliminates the use of `promiseLibrary` stores within class constructors. Creates `PromiseProvider.get()` hook to retrieve the latest promise-library state. Allows setter of promise-library on module export `mongodb.Promise`. Preserves `promiseLibrary` option for `MongoClient`. Adds "no-native-promise" eslint rule to prevent direct internal use of native promises. NODE-2579
1 parent 491e23b commit e5b762c

26 files changed

+167
-156
lines changed

.eslintrc

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
"parserOptions": {
99
"ecmaVersion": 2018
1010
},
11-
"plugins": ["prettier", "jsdoc"],
11+
"plugins": ["prettier", "promise", "jsdoc"],
1212
"rules": {
1313
"prettier/prettier": "error",
1414

@@ -30,8 +30,17 @@
3030

3131
"no-console": "off",
3232
"eqeqeq": ["error", "always", { "null": "ignore" }],
33-
"strict": ["error", "global"]
33+
"strict": ["error", "global"],
34+
"promise/no-native": "error"
3435
},
36+
"overrides": [
37+
{
38+
"files": ["test/**/*.js"],
39+
"rules": {
40+
"promise/no-native": "off"
41+
}
42+
}
43+
],
3544
"settings": {
3645
"jsdoc": {
3746
"check-types": false,

index.js

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,23 @@
11
'use strict';
2+
23
const error = require('./lib/error');
34
const Instrumentation = require('./lib/apm');
45
const { BSON } = require('./lib/deps');
56
const { Cursor, AggregationCursor, CommandCursor } = require('./lib/cursor');
7+
const PromiseProvider = require('./lib/promise_provider');
68

79
// Set up the connect function
810
const connect = require('./lib/mongo_client').connect;
911

12+
Object.defineProperty(connect, 'Promise', {
13+
get: function() {
14+
return PromiseProvider.get();
15+
},
16+
set: function(lib) {
17+
PromiseProvider.set(lib);
18+
}
19+
});
20+
1021
// Expose error class
1122
connect.MongoError = error.MongoError;
1223
connect.MongoNetworkError = error.MongoNetworkError;

lib/admin.js

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -44,16 +44,14 @@ const executeOperation = require('./operations/execute_operation');
4444
* @returns {Admin} a collection instance.
4545
* @param {any} db
4646
* @param {any} topology
47-
* @param {any} promiseLibrary
4847
*/
49-
function Admin(db, topology, promiseLibrary) {
48+
function Admin(db, topology) {
5049
if (!(this instanceof Admin)) return new Admin(db, topology);
5150

5251
// Internal state
5352
this.s = {
5453
db: db,
55-
topology: topology,
56-
promiseLibrary: promiseLibrary
54+
topology: topology
5755
};
5856
}
5957

lib/bulk/common.js

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
'use strict';
22

3+
const PromiseProvider = require('../promise_provider');
34
const {
45
BSON: { Long, ObjectId }
56
} = require('../deps');
@@ -781,9 +782,6 @@ class BulkOperationBase {
781782
finalOptions = applyWriteConcern(finalOptions, { collection: collection }, options);
782783
const writeConcern = finalOptions.writeConcern;
783784

784-
// Get the promiseLibrary
785-
const promiseLibrary = options.promiseLibrary || Promise;
786-
787785
// Final results
788786
const bulkResult = {
789787
ok: 1,
@@ -832,8 +830,6 @@ class BulkOperationBase {
832830
executed: executed,
833831
// Collection
834832
collection: collection,
835-
// Promise Library
836-
promiseLibrary: promiseLibrary,
837833
// Fundamental error
838834
err: null,
839835
// check keys
@@ -1027,12 +1023,14 @@ class BulkOperationBase {
10271023
* @param {any} callback
10281024
*/
10291025
_handleEarlyError(err, callback) {
1026+
const Promise = PromiseProvider.get();
1027+
10301028
if (typeof callback === 'function') {
10311029
callback(err, null);
10321030
return;
10331031
}
10341032

1035-
return this.s.promiseLibrary.reject(err);
1033+
return Promise.reject(err);
10361034
}
10371035

10381036
/**

lib/change_stream.js

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -85,7 +85,6 @@ class ChangeStream extends EventEmitter {
8585
);
8686
}
8787

88-
this.promiseLibrary = parent.s.promiseLibrary;
8988
if (!this.options.readPreference && parent.s.readPreference) {
9089
this.options.readPreference = parent.s.readPreference;
9190
}
@@ -130,7 +129,7 @@ class ChangeStream extends EventEmitter {
130129
* @returns {Promise|void} returns Promise if no callback passed
131130
*/
132131
hasNext(callback) {
133-
return maybePromise(this.parent, callback, cb => this.cursor.hasNext(cb));
132+
return maybePromise(callback, cb => this.cursor.hasNext(cb));
134133
}
135134

136135
/**
@@ -142,7 +141,7 @@ class ChangeStream extends EventEmitter {
142141
* @returns {Promise|void} returns Promise if no callback passed
143142
*/
144143
next(callback) {
145-
return maybePromise(this.parent, callback, cb => {
144+
return maybePromise(callback, cb => {
146145
if (this.isClosed()) {
147146
return cb(new MongoError('ChangeStream is closed'));
148147
}
@@ -170,7 +169,7 @@ class ChangeStream extends EventEmitter {
170169
* @returns {Promise} returns Promise if no callback passed
171170
*/
172171
close(callback) {
173-
return maybePromise(this.parent, callback, cb => {
172+
return maybePromise(callback, cb => {
174173
if (this.closed) return cb();
175174

176175
// flag the change stream as explicitly closed

lib/collection.js

Lines changed: 11 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
'use strict';
22

3+
const { emitDeprecatedOptionWarning } = require('./utils');
4+
const PromiseProvider = require('./promise_provider');
35
const ReadPreference = require('./read_preference');
46
const { deprecate } = require('util');
57
const {
@@ -111,6 +113,7 @@ const mergeKeys = ['ignoreUndefined'];
111113
*/
112114
function Collection(db, topology, dbName, name, pkFactory, options) {
113115
checkCollectionName(name);
116+
emitDeprecatedOptionWarning(options, ['promiseLibrary']);
114117

115118
// Unpack variables
116119
const internalHint = null;
@@ -136,9 +139,6 @@ function Collection(db, topology, dbName, name, pkFactory, options) {
136139

137140
const namespace = new MongoDBNamespace(dbName, name);
138141

139-
// Get the promiseLibrary
140-
const promiseLibrary = options.promiseLibrary || Promise;
141-
142142
// Set custom primary key factory if provided
143143
pkFactory = pkFactory == null ? ObjectId : pkFactory;
144144

@@ -172,8 +172,6 @@ function Collection(db, topology, dbName, name, pkFactory, options) {
172172
internalHint: internalHint,
173173
// collectionHint
174174
collectionHint: collectionHint,
175-
// Promise library
176-
promiseLibrary: promiseLibrary,
177175
// Read Concern
178176
readConcern: ReadConcern.fromOptions(options),
179177
// Write Concern
@@ -453,9 +451,6 @@ Collection.prototype.find = deprecateOptions(
453451
// Add db object to the new options
454452
newOptions.db = this.s.db;
455453

456-
// Add the promise library
457-
newOptions.promiseLibrary = this.s.promiseLibrary;
458-
459454
// Set raw if available at collection level
460455
if (newOptions.raw == null && typeof this.s.raw === 'boolean') newOptions.raw = this.s.raw;
461456
// Set promoteLongs if available at collection level
@@ -757,11 +752,12 @@ Collection.prototype.insert = deprecate(function(docs, options, callback) {
757752
Collection.prototype.updateOne = function(filter, update, options, callback) {
758753
if (typeof options === 'function') (callback = options), (options = {});
759754
options = options || {};
755+
const Promise = PromiseProvider.get();
760756

761757
const err = checkForAtomicOperators(update);
762758
if (err) {
763759
if (typeof callback === 'function') return callback(err);
764-
return this.s.promiseLibrary.reject(err);
760+
return Promise.reject(err);
765761
}
766762

767763
options = Object.assign({}, options);
@@ -836,13 +832,14 @@ Collection.prototype.replaceOne = function(filter, doc, options, callback) {
836832
* @returns {Promise<Collection~updateWriteOpResult>} returns Promise if no callback passed
837833
*/
838834
Collection.prototype.updateMany = function(filter, update, options, callback) {
835+
const Promise = PromiseProvider.get();
839836
if (typeof options === 'function') (callback = options), (options = {});
840837
options = options || {};
841838

842839
const err = checkForAtomicOperators(update);
843840
if (err) {
844841
if (typeof callback === 'function') return callback(err);
845-
return this.s.promiseLibrary.reject(err);
842+
return Promise.reject(err);
846843
}
847844

848845
options = Object.assign({}, options);
@@ -1786,6 +1783,8 @@ Collection.prototype.findOneAndReplace = function(filter, replacement, options,
17861783
* @returns {Promise<Collection~findAndModifyWriteOpResultObject>} returns Promise if no callback passed
17871784
*/
17881785
Collection.prototype.findOneAndUpdate = function(filter, update, options, callback) {
1786+
const Promise = PromiseProvider.get();
1787+
17891788
if (typeof options === 'function') (callback = options), (options = {});
17901789
options = options || {};
17911790

@@ -1798,7 +1797,7 @@ Collection.prototype.findOneAndUpdate = function(filter, update, options, callba
17981797
const err = checkForAtomicOperators(update);
17991798
if (err) {
18001799
if (typeof callback === 'function') return callback(err);
1801-
return this.s.promiseLibrary.reject(err);
1800+
return Promise.reject(err);
18021801
}
18031802

18041803
const findOneAndUpdateOperation = new FindOneAndUpdateOperation(this, filter, update, options);
@@ -2018,9 +2017,6 @@ Collection.prototype.parallelCollectionScan = deprecate(function(options, callba
20182017
// Ensure we have the right read preference inheritance
20192018
options.readPreference = resolveReadPreference(this, options);
20202019

2021-
// Add a promiseLibrary
2022-
options.promiseLibrary = this.s.promiseLibrary;
2023-
20242020
if (options.session) {
20252021
options.session = undefined;
20262022
}
@@ -2174,7 +2170,6 @@ Collection.prototype.initializeUnorderedBulkOp = function(options) {
21742170
options.ignoreUndefined = this.s.options.ignoreUndefined;
21752171
}
21762172

2177-
options.promiseLibrary = this.s.promiseLibrary;
21782173
return unordered(this.s.topology, this, options);
21792174
};
21802175

@@ -2196,7 +2191,7 @@ Collection.prototype.initializeOrderedBulkOp = function(options) {
21962191
if (options.ignoreUndefined == null) {
21972192
options.ignoreUndefined = this.s.options.ignoreUndefined;
21982193
}
2199-
options.promiseLibrary = this.s.promiseLibrary;
2194+
22002195
return ordered(this.s.topology, this, options);
22012196
};
22022197

lib/cursor/cursor.js

Lines changed: 14 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
'use strict';
22

3+
const { emitDeprecatedOptionWarning } = require('../utils');
4+
const PromiseProvider = require('../promise_provider');
35
const ReadPreference = require('../read_preference');
46
const { Transform, PassThrough } = require('stream');
57
const { deprecate } = require('util');
@@ -101,14 +103,13 @@ class Cursor extends CoreCursor {
101103
options = this.operation.options;
102104
}
103105

106+
emitDeprecatedOptionWarning(options, ['promiseLibrary']);
107+
104108
// Tailable cursor options
105109
const numberOfRetries = options.numberOfRetries || 5;
106110
const tailableRetryInterval = options.tailableRetryInterval || 500;
107111
const currentNumberOfRetries = numberOfRetries;
108112

109-
// Get the promiseLibrary
110-
const promiseLibrary = options.promiseLibrary || Promise;
111-
112113
// Internal cursor state
113114
this.s = {
114115
// Tailable cursor options
@@ -117,8 +118,6 @@ class Cursor extends CoreCursor {
117118
currentNumberOfRetries: currentNumberOfRetries,
118119
// State
119120
state: CursorState.INIT,
120-
// Promise library
121-
promiseLibrary,
122121
// explicitlyIgnoreSession
123122
explicitlyIgnoreSession: !!options.explicitlyIgnoreSession
124123
};
@@ -193,7 +192,7 @@ class Cursor extends CoreCursor {
193192
throw MongoError.create({ message: 'Cursor is closed', driver: true });
194193
}
195194

196-
return maybePromise(this, callback, cb => {
195+
return maybePromise(callback, cb => {
197196
const cursor = this;
198197
if (cursor.isNotified()) {
199198
return cb(null, false);
@@ -221,7 +220,7 @@ class Cursor extends CoreCursor {
221220
* @returns {Promise} returns Promise if no callback passed
222221
*/
223222
next(callback) {
224-
return maybePromise(this, callback, cb => {
223+
return maybePromise(callback, cb => {
225224
const cursor = this;
226225
if (cursor.s.state === CursorState.CLOSED || (cursor.isDead && cursor.isDead())) {
227226
cb(MongoError.create({ message: 'Cursor is closed', driver: true }));
@@ -746,6 +745,7 @@ class Cursor extends CoreCursor {
746745
* @returns {Promise} if no callback supplied
747746
*/
748747
forEach(iterator, callback) {
748+
const Promise = PromiseProvider.get();
749749
// Rewind cursor state
750750
this.rewind();
751751

@@ -770,7 +770,7 @@ class Cursor extends CoreCursor {
770770
}
771771
});
772772
} else {
773-
return new this.s.promiseLibrary((fulfill, reject) => {
773+
return new Promise((fulfill, reject) => {
774774
each(this, (err, doc) => {
775775
if (err) {
776776
reject(err);
@@ -840,7 +840,7 @@ class Cursor extends CoreCursor {
840840
driver: true
841841
});
842842
}
843-
return maybePromise(this, callback, cb => {
843+
return maybePromise(callback, cb => {
844844
const cursor = this;
845845
const items = [];
846846

@@ -934,6 +934,8 @@ class Cursor extends CoreCursor {
934934
* @returns {Promise} returns Promise if no callback passed
935935
*/
936936
close(options, callback) {
937+
const Promise = PromiseProvider.get();
938+
937939
if (typeof options === 'function') (callback = options), (options = {});
938940
options = Object.assign({}, { skipKillCursors: false }, options);
939941

@@ -953,7 +955,7 @@ class Cursor extends CoreCursor {
953955
}
954956

955957
// Return a Promise
956-
return new this.s.promiseLibrary(resolve => {
958+
return new Promise(resolve => {
957959
resolve();
958960
});
959961
};
@@ -963,7 +965,7 @@ class Cursor extends CoreCursor {
963965
return this._endSession(() => completeClose());
964966
}
965967

966-
return new this.s.promiseLibrary(resolve => {
968+
return new Promise(resolve => {
967969
this._endSession(() => completeClose().then(resolve));
968970
});
969971
}
@@ -1069,7 +1071,7 @@ class Cursor extends CoreCursor {
10691071
if (this.cmd.readConcern) {
10701072
delete this.cmd['readConcern'];
10711073
}
1072-
return maybePromise(this, callback, cb => {
1074+
return maybePromise(callback, cb => {
10731075
CoreCursor.prototype._next.apply(this, [cb]);
10741076
});
10751077
}

0 commit comments

Comments
 (0)