From 17e87cf890bbd392cf45fb1c7da12b2b5e6fb72f Mon Sep 17 00:00:00 2001 From: Neal Beeken Date: Wed, 4 Jan 2023 17:35:03 -0500 Subject: [PATCH 01/34] test(NODE-4919): import mongodb-legacy in tests --- test/unit/mongodb-legacy.test.ts | 9 +++++++++ 1 file changed, 9 insertions(+) create mode 100644 test/unit/mongodb-legacy.test.ts diff --git a/test/unit/mongodb-legacy.test.ts b/test/unit/mongodb-legacy.test.ts new file mode 100644 index 00000000000..2bcceeb0e0e --- /dev/null +++ b/test/unit/mongodb-legacy.test.ts @@ -0,0 +1,9 @@ +import { expect } from 'chai'; + +import { Db } from '../mongodb'; + +describe('mongodb-legacy', () => { + it('imports a Db with the legacy symbol', () => { + expect(Db.prototype).to.have.property(Symbol.for('@@mdb.callbacks.toLegacy')); + }); +}); From 82ac2b9886885127366a3dd750ee4c24dcc0f17b Mon Sep 17 00:00:00 2001 From: Neal Beeken Date: Thu, 19 Jan 2023 18:02:47 -0500 Subject: [PATCH 02/34] move test file, update coverage --- test/unit/mongodb-legacy.test.ts | 9 --------- 1 file changed, 9 deletions(-) delete mode 100644 test/unit/mongodb-legacy.test.ts diff --git a/test/unit/mongodb-legacy.test.ts b/test/unit/mongodb-legacy.test.ts deleted file mode 100644 index 2bcceeb0e0e..00000000000 --- a/test/unit/mongodb-legacy.test.ts +++ /dev/null @@ -1,9 +0,0 @@ -import { expect } from 'chai'; - -import { Db } from '../mongodb'; - -describe('mongodb-legacy', () => { - it('imports a Db with the legacy symbol', () => { - expect(Db.prototype).to.have.property(Symbol.for('@@mdb.callbacks.toLegacy')); - }); -}); From 4ac5a2f45b9b9b2dd44d3dd4b57b93704e5fab06 Mon Sep 17 00:00:00 2001 From: Neal Beeken Date: Wed, 4 Jan 2023 17:35:03 -0500 Subject: [PATCH 03/34] test(NODE-4919): import mongodb-legacy in tests --- test/unit/mongodb-legacy.test.ts | 9 +++++++++ 1 file changed, 9 insertions(+) create mode 100644 test/unit/mongodb-legacy.test.ts diff --git a/test/unit/mongodb-legacy.test.ts b/test/unit/mongodb-legacy.test.ts new file mode 100644 index 00000000000..2bcceeb0e0e --- /dev/null +++ b/test/unit/mongodb-legacy.test.ts @@ -0,0 +1,9 @@ +import { expect } from 'chai'; + +import { Db } from '../mongodb'; + +describe('mongodb-legacy', () => { + it('imports a Db with the legacy symbol', () => { + expect(Db.prototype).to.have.property(Symbol.for('@@mdb.callbacks.toLegacy')); + }); +}); From d71a30220d981a2c22c6b9c93bf40d9271dd8886 Mon Sep 17 00:00:00 2001 From: Neal Beeken Date: Tue, 20 Dec 2022 10:31:20 -0500 Subject: [PATCH 04/34] feat: remove callbacks from admin.ts --- src/admin.ts | 215 ++++---------------------- src/operations/validate_collection.ts | 3 +- 2 files changed, 34 insertions(+), 184 deletions(-) diff --git a/src/admin.ts b/src/admin.ts index 977de4e8852..711cc90e681 100644 --- a/src/admin.ts +++ b/src/admin.ts @@ -14,7 +14,6 @@ import { ValidateCollectionOperation, ValidateCollectionOptions } from './operations/validate_collection'; -import type { Callback } from './utils'; /** @internal */ export interface AdminPrivate { @@ -57,26 +56,11 @@ export class Admin { * * @param command - The command to execute * @param options - Optional settings for the command - * @param callback - An optional callback, a Promise will be returned if none is provided */ - command(command: Document): Promise; - command(command: Document, options: RunCommandOptions): Promise; - /** @deprecated Callbacks are deprecated and will be removed in the next major version. See [mongodb-legacy](https://github.com/mongodb-js/nodejs-mongodb-legacy) for migration assistance */ - command(command: Document, callback: Callback): void; - /** @deprecated Callbacks are deprecated and will be removed in the next major version. See [mongodb-legacy](https://github.com/mongodb-js/nodejs-mongodb-legacy) for migration assistance */ - command(command: Document, options: RunCommandOptions, callback: Callback): void; - command( - command: Document, - options?: RunCommandOptions | Callback, - callback?: Callback - ): Promise | void { - if (typeof options === 'function') (callback = options), (options = {}); - options = Object.assign({ dbName: 'admin' }, options); - + async command(command: Document, options?: RunCommandOptions): Promise { return executeOperation( this.s.db.s.client, - new RunCommandOperation(this.s.db, command, options), - callback + new RunCommandOperation(this.s.db, command, { dbName: 'admin', ...options }) ); } @@ -84,84 +68,36 @@ export class Admin { * Retrieve the server build information * * @param options - Optional settings for the command - * @param callback - An optional callback, a Promise will be returned if none is provided */ - buildInfo(): Promise; - buildInfo(options: CommandOperationOptions): Promise; - /** @deprecated Callbacks are deprecated and will be removed in the next major version. See [mongodb-legacy](https://github.com/mongodb-js/nodejs-mongodb-legacy) for migration assistance */ - buildInfo(callback: Callback): void; - /** @deprecated Callbacks are deprecated and will be removed in the next major version. See [mongodb-legacy](https://github.com/mongodb-js/nodejs-mongodb-legacy) for migration assistance */ - buildInfo(options: CommandOperationOptions, callback: Callback): void; - buildInfo( - options?: CommandOperationOptions | Callback, - callback?: Callback - ): Promise | void { - if (typeof options === 'function') (callback = options), (options = {}); - options = options ?? {}; - return this.command({ buildinfo: 1 }, options, callback as Callback); + async buildInfo(options?: CommandOperationOptions): Promise { + return this.command({ buildinfo: 1 }, options); } /** * Retrieve the server build information * * @param options - Optional settings for the command - * @param callback - An optional callback, a Promise will be returned if none is provided */ - serverInfo(): Promise; - serverInfo(options: CommandOperationOptions): Promise; - /** @deprecated Callbacks are deprecated and will be removed in the next major version. See [mongodb-legacy](https://github.com/mongodb-js/nodejs-mongodb-legacy) for migration assistance */ - serverInfo(callback: Callback): void; - /** @deprecated Callbacks are deprecated and will be removed in the next major version. See [mongodb-legacy](https://github.com/mongodb-js/nodejs-mongodb-legacy) for migration assistance */ - serverInfo(options: CommandOperationOptions, callback: Callback): void; - serverInfo( - options?: CommandOperationOptions | Callback, - callback?: Callback - ): Promise | void { - if (typeof options === 'function') (callback = options), (options = {}); - options = options ?? {}; - return this.command({ buildinfo: 1 }, options, callback as Callback); + async serverInfo(options?: CommandOperationOptions): Promise { + return this.command({ buildinfo: 1 }, options); } /** * Retrieve this db's server status. * * @param options - Optional settings for the command - * @param callback - An optional callback, a Promise will be returned if none is provided */ - serverStatus(): Promise; - serverStatus(options: CommandOperationOptions): Promise; - /** @deprecated Callbacks are deprecated and will be removed in the next major version. See [mongodb-legacy](https://github.com/mongodb-js/nodejs-mongodb-legacy) for migration assistance */ - serverStatus(callback: Callback): void; - /** @deprecated Callbacks are deprecated and will be removed in the next major version. See [mongodb-legacy](https://github.com/mongodb-js/nodejs-mongodb-legacy) for migration assistance */ - serverStatus(options: CommandOperationOptions, callback: Callback): void; - serverStatus( - options?: CommandOperationOptions | Callback, - callback?: Callback - ): Promise | void { - if (typeof options === 'function') (callback = options), (options = {}); - options = options ?? {}; - return this.command({ serverStatus: 1 }, options, callback as Callback); + async serverStatus(options?: CommandOperationOptions): Promise { + return this.command({ serverStatus: 1 }, options); } /** * Ping the MongoDB server and retrieve results * * @param options - Optional settings for the command - * @param callback - An optional callback, a Promise will be returned if none is provided */ - ping(): Promise; - ping(options: CommandOperationOptions): Promise; - /** @deprecated Callbacks are deprecated and will be removed in the next major version. See [mongodb-legacy](https://github.com/mongodb-js/nodejs-mongodb-legacy) for migration assistance */ - ping(callback: Callback): void; - /** @deprecated Callbacks are deprecated and will be removed in the next major version. See [mongodb-legacy](https://github.com/mongodb-js/nodejs-mongodb-legacy) for migration assistance */ - ping(options: CommandOperationOptions, callback: Callback): void; - ping( - options?: CommandOperationOptions | Callback, - callback?: Callback - ): Promise | void { - if (typeof options === 'function') (callback = options), (options = {}); - options = options ?? {}; - return this.command({ ping: 1 }, options, callback as Callback); + async ping(options?: CommandOperationOptions): Promise { + return this.command({ ping: 1 }, options); } /** @@ -170,49 +106,22 @@ export class Admin { * @param username - The username for the new user * @param password - An optional password for the new user * @param options - Optional settings for the command - * @param callback - An optional callback, a Promise will be returned if none is provided */ - addUser(username: string): Promise; - addUser(username: string, password: string): Promise; - addUser(username: string, options: AddUserOptions): Promise; - addUser(username: string, password: string, options: AddUserOptions): Promise; - /** @deprecated Callbacks are deprecated and will be removed in the next major version. See [mongodb-legacy](https://github.com/mongodb-js/nodejs-mongodb-legacy) for migration assistance */ - addUser(username: string, callback: Callback): void; - /** @deprecated Callbacks are deprecated and will be removed in the next major version. See [mongodb-legacy](https://github.com/mongodb-js/nodejs-mongodb-legacy) for migration assistance */ - addUser(username: string, password: string, callback: Callback): void; - /** @deprecated Callbacks are deprecated and will be removed in the next major version. See [mongodb-legacy](https://github.com/mongodb-js/nodejs-mongodb-legacy) for migration assistance */ - addUser(username: string, options: AddUserOptions, callback: Callback): void; - /** @deprecated Callbacks are deprecated and will be removed in the next major version. See [mongodb-legacy](https://github.com/mongodb-js/nodejs-mongodb-legacy) for migration assistance */ - addUser( - username: string, - password: string, - options: AddUserOptions, - callback: Callback - ): void; - addUser( + async addUser( username: string, - password?: string | AddUserOptions | Callback, - options?: AddUserOptions | Callback, - callback?: Callback - ): Promise | void { - if (typeof password === 'function') { - (callback = password), (password = undefined), (options = {}); - } else if (typeof password !== 'string') { - if (typeof options === 'function') { - (callback = options), (options = password), (password = undefined); - } else { - (options = password), (callback = undefined), (password = undefined); - } - } else { - if (typeof options === 'function') (callback = options), (options = {}); - } - - options = Object.assign({ dbName: 'admin' }, options); - + password?: string | AddUserOptions, + options?: AddUserOptions + ): Promise { + options = + options != null && typeof options === 'object' + ? options + : password != null && typeof password === 'object' + ? password + : undefined; + password = typeof password === 'string' ? password : undefined; return executeOperation( this.s.db.s.client, - new AddUserOperation(this.s.db, username, password, options), - callback + new AddUserOperation(this.s.db, username, password, { dbName: 'admin', ...options }) ); } @@ -221,26 +130,11 @@ export class Admin { * * @param username - The username to remove * @param options - Optional settings for the command - * @param callback - An optional callback, a Promise will be returned if none is provided */ - removeUser(username: string): Promise; - removeUser(username: string, options: RemoveUserOptions): Promise; - /** @deprecated Callbacks are deprecated and will be removed in the next major version. See [mongodb-legacy](https://github.com/mongodb-js/nodejs-mongodb-legacy) for migration assistance */ - removeUser(username: string, callback: Callback): void; - /** @deprecated Callbacks are deprecated and will be removed in the next major version. See [mongodb-legacy](https://github.com/mongodb-js/nodejs-mongodb-legacy) for migration assistance */ - removeUser(username: string, options: RemoveUserOptions, callback: Callback): void; - removeUser( - username: string, - options?: RemoveUserOptions | Callback, - callback?: Callback - ): Promise | void { - if (typeof options === 'function') (callback = options), (options = {}); - options = Object.assign({ dbName: 'admin' }, options); - + async removeUser(username: string, options?: RemoveUserOptions): Promise { return executeOperation( this.s.db.s.client, - new RemoveUserOperation(this.s.db, username, options), - callback + new RemoveUserOperation(this.s.db, username, { dbName: 'admin', ...options }) ); } @@ -249,30 +143,14 @@ export class Admin { * * @param collectionName - The name of the collection to validate. * @param options - Optional settings for the command - * @param callback - An optional callback, a Promise will be returned if none is provided */ - validateCollection(collectionName: string): Promise; - validateCollection(collectionName: string, options: ValidateCollectionOptions): Promise; - /** @deprecated Callbacks are deprecated and will be removed in the next major version. See [mongodb-legacy](https://github.com/mongodb-js/nodejs-mongodb-legacy) for migration assistance */ - validateCollection(collectionName: string, callback: Callback): void; - /** @deprecated Callbacks are deprecated and will be removed in the next major version. See [mongodb-legacy](https://github.com/mongodb-js/nodejs-mongodb-legacy) for migration assistance */ - validateCollection( + async validateCollection( collectionName: string, - options: ValidateCollectionOptions, - callback: Callback - ): void; - validateCollection( - collectionName: string, - options?: ValidateCollectionOptions | Callback, - callback?: Callback - ): Promise | void { - if (typeof options === 'function') (callback = options), (options = {}); - options = options ?? {}; - + options?: ValidateCollectionOptions + ): Promise { return executeOperation( this.s.db.s.client, - new ValidateCollectionOperation(this, collectionName, options), - callback + new ValidateCollectionOperation(this, collectionName, options) ); } @@ -280,46 +158,17 @@ export class Admin { * List the available databases * * @param options - Optional settings for the command - * @param callback - An optional callback, a Promise will be returned if none is provided */ - listDatabases(): Promise; - listDatabases(options: ListDatabasesOptions): Promise; - /** @deprecated Callbacks are deprecated and will be removed in the next major version. See [mongodb-legacy](https://github.com/mongodb-js/nodejs-mongodb-legacy) for migration assistance */ - listDatabases(callback: Callback): void; - /** @deprecated Callbacks are deprecated and will be removed in the next major version. See [mongodb-legacy](https://github.com/mongodb-js/nodejs-mongodb-legacy) for migration assistance */ - listDatabases(options: ListDatabasesOptions, callback: Callback): void; - listDatabases( - options?: ListDatabasesOptions | Callback, - callback?: Callback - ): Promise | void { - if (typeof options === 'function') (callback = options), (options = {}); - options = options ?? {}; - - return executeOperation( - this.s.db.s.client, - new ListDatabasesOperation(this.s.db, options), - callback - ); + async listDatabases(options?: ListDatabasesOptions): Promise { + return executeOperation(this.s.db.s.client, new ListDatabasesOperation(this.s.db, options)); } /** * Get ReplicaSet status * * @param options - Optional settings for the command - * @param callback - An optional callback, a Promise will be returned if none is provided */ - replSetGetStatus(): Promise; - replSetGetStatus(options: CommandOperationOptions): Promise; - /** @deprecated Callbacks are deprecated and will be removed in the next major version. See [mongodb-legacy](https://github.com/mongodb-js/nodejs-mongodb-legacy) for migration assistance */ - replSetGetStatus(callback: Callback): void; - /** @deprecated Callbacks are deprecated and will be removed in the next major version. See [mongodb-legacy](https://github.com/mongodb-js/nodejs-mongodb-legacy) for migration assistance */ - replSetGetStatus(options: CommandOperationOptions, callback: Callback): void; - replSetGetStatus( - options?: CommandOperationOptions | Callback, - callback?: Callback - ): Promise | void { - if (typeof options === 'function') (callback = options), (options = {}); - options = options ?? {}; - return this.command({ replSetGetStatus: 1 }, options, callback as Callback); + async replSetGetStatus(options?: CommandOperationOptions): Promise { + return this.command({ replSetGetStatus: 1 }, options); } } diff --git a/src/operations/validate_collection.ts b/src/operations/validate_collection.ts index ba6c85fad05..fee124f4781 100644 --- a/src/operations/validate_collection.ts +++ b/src/operations/validate_collection.ts @@ -18,8 +18,9 @@ export class ValidateCollectionOperation extends CommandOperation { collectionName: string; command: Document; - constructor(admin: Admin, collectionName: string, options: ValidateCollectionOptions) { + constructor(admin: Admin, collectionName: string, options?: ValidateCollectionOptions) { // Decorate command with extra options + options ??= {}; const command: Document = { validate: collectionName }; const keys = Object.keys(options); for (let i = 0; i < keys.length; i++) { From b1d5971adeaafb3c97d3b0605f7937adcf7d574a Mon Sep 17 00:00:00 2001 From: Neal Beeken Date: Tue, 20 Dec 2022 10:38:15 -0500 Subject: [PATCH 05/34] feat: remove callbacks from bulk/common.ts --- src/bulk/common.ts | 75 +++++++++++++++++----------------------------- 1 file changed, 27 insertions(+), 48 deletions(-) diff --git a/src/bulk/common.ts b/src/bulk/common.ts index 1d1ade6e506..51ad8256781 100644 --- a/src/bulk/common.ts +++ b/src/bulk/common.ts @@ -23,7 +23,6 @@ import { Callback, getTopology, hasAtomicOperators, - maybeCallback, MongoDBNamespace, resolveOptions } from '../utils'; @@ -1175,56 +1174,36 @@ export abstract class BulkOperationBase { return batches; } - execute(options?: BulkWriteOptions): Promise; - /** @deprecated Callbacks are deprecated and will be removed in the next major version. See [mongodb-legacy](https://github.com/mongodb-js/nodejs-mongodb-legacy) for migration assistance */ - execute(callback: Callback): void; - /** @deprecated Callbacks are deprecated and will be removed in the next major version. See [mongodb-legacy](https://github.com/mongodb-js/nodejs-mongodb-legacy) for migration assistance */ - execute(options: BulkWriteOptions | undefined, callback: Callback): void; - /** @deprecated Callbacks are deprecated and will be removed in the next major version. See [mongodb-legacy](https://github.com/mongodb-js/nodejs-mongodb-legacy) for migration assistance */ - execute( - options?: BulkWriteOptions | Callback, - callback?: Callback - ): Promise | void; - execute( - options?: BulkWriteOptions | Callback, - callback?: Callback - ): Promise | void { - callback = - typeof callback === 'function' - ? callback - : typeof options === 'function' - ? options - : undefined; - return maybeCallback(async () => { - options = options != null && typeof options !== 'function' ? options : {}; - - if (this.s.executed) { - throw new MongoBatchReExecutionError(); - } + async execute(options?: BulkWriteOptions): Promise { + options ??= {}; - const writeConcern = WriteConcern.fromOptions(options); - if (writeConcern) { - this.s.writeConcern = writeConcern; - } + if (this.s.executed) { + throw new MongoBatchReExecutionError(); + } - // If we have current batch - if (this.isOrdered) { - if (this.s.currentBatch) this.s.batches.push(this.s.currentBatch); - } else { - if (this.s.currentInsertBatch) this.s.batches.push(this.s.currentInsertBatch); - if (this.s.currentUpdateBatch) this.s.batches.push(this.s.currentUpdateBatch); - if (this.s.currentRemoveBatch) this.s.batches.push(this.s.currentRemoveBatch); - } - // If we have no operations in the bulk raise an error - if (this.s.batches.length === 0) { - throw new MongoInvalidArgumentError('Invalid BulkOperation, Batch cannot be empty'); - } + const writeConcern = WriteConcern.fromOptions(options); + if (writeConcern) { + this.s.writeConcern = writeConcern; + } + + // If we have current batch + if (this.isOrdered) { + if (this.s.currentBatch) this.s.batches.push(this.s.currentBatch); + } else { + if (this.s.currentInsertBatch) this.s.batches.push(this.s.currentInsertBatch); + if (this.s.currentUpdateBatch) this.s.batches.push(this.s.currentUpdateBatch); + if (this.s.currentRemoveBatch) this.s.batches.push(this.s.currentRemoveBatch); + } + // If we have no operations in the bulk raise an error + if (this.s.batches.length === 0) { + throw new MongoInvalidArgumentError('Invalid BulkOperation, Batch cannot be empty'); + } + + this.s.executed = true; + const finalOptions = { ...this.s.options, ...options }; + const operation = new BulkWriteShimOperation(this, finalOptions); - this.s.executed = true; - const finalOptions = { ...this.s.options, ...options }; - const operation = new BulkWriteShimOperation(this, finalOptions); - return executeOperation(this.s.collection.s.db.s.client, operation); - }, callback); + return executeOperation(this.s.collection.s.db.s.client, operation); } /** From af01cc7b3941c05aeaf692e3497caf4b695858f3 Mon Sep 17 00:00:00 2001 From: Neal Beeken Date: Tue, 20 Dec 2022 11:01:01 -0500 Subject: [PATCH 06/34] feat: remove callbacks from change_stream.ts --- src/change_stream.ts | 138 ++++++++++++++++++------------------------- 1 file changed, 58 insertions(+), 80 deletions(-) diff --git a/src/change_stream.ts b/src/change_stream.ts index e346609e091..ee6876acc1c 100644 --- a/src/change_stream.ts +++ b/src/change_stream.ts @@ -19,7 +19,7 @@ import type { AggregateOptions } from './operations/aggregate'; import type { CollationOptions, OperationParent } from './operations/command'; import type { ReadPreference } from './read_preference'; import type { ServerSessionId } from './sessions'; -import { Callback, filterOptions, getTopology, maybeCallback, MongoDBNamespace } from './utils'; +import { filterOptions, getTopology, MongoDBNamespace } from './utils'; /** @internal */ const kCursorStream = Symbol('cursorStream'); @@ -637,99 +637,84 @@ export class ChangeStream< } /** Check if there is any document still available in the Change Stream */ - hasNext(): Promise; - /** @deprecated Callbacks are deprecated and will be removed in the next major version. See [mongodb-legacy](https://github.com/mongodb-js/nodejs-mongodb-legacy) for migration assistance */ - hasNext(callback: Callback): void; - hasNext(callback?: Callback): Promise | void { + async hasNext(): Promise { this._setIsIterator(); - return maybeCallback(async () => { - // Change streams must resume indefinitely while each resume event succeeds. - // This loop continues until either a change event is received or until a resume attempt - // fails. - // eslint-disable-next-line no-constant-condition - while (true) { + // Change streams must resume indefinitely while each resume event succeeds. + // This loop continues until either a change event is received or until a resume attempt + // fails. + // eslint-disable-next-line no-constant-condition + while (true) { + try { + const hasNext = await this.cursor.hasNext(); + return hasNext; + } catch (error) { try { - const hasNext = await this.cursor.hasNext(); - return hasNext; + await this._processErrorIteratorMode(error); } catch (error) { try { - await this._processErrorIteratorMode(error); - } catch (error) { - try { - await this.close(); - } catch { - // We are not concerned with errors from close() - } - throw error; + await this.close(); + } catch { + // We are not concerned with errors from close() } + throw error; } } - }, callback); + } } /** Get the next available document from the Change Stream. */ - next(): Promise; - /** @deprecated Callbacks are deprecated and will be removed in the next major version. See [mongodb-legacy](https://github.com/mongodb-js/nodejs-mongodb-legacy) for migration assistance */ - next(callback: Callback): void; - next(callback?: Callback): Promise | void { + async next(): Promise { this._setIsIterator(); - return maybeCallback(async () => { - // Change streams must resume indefinitely while each resume event succeeds. - // This loop continues until either a change event is received or until a resume attempt - // fails. - // eslint-disable-next-line no-constant-condition - while (true) { + // Change streams must resume indefinitely while each resume event succeeds. + // This loop continues until either a change event is received or until a resume attempt + // fails. + // eslint-disable-next-line no-constant-condition + while (true) { + try { + const change = await this.cursor.next(); + const processedChange = this._processChange(change ?? null); + return processedChange; + } catch (error) { try { - const change = await this.cursor.next(); - const processedChange = this._processChange(change ?? null); - return processedChange; + await this._processErrorIteratorMode(error); } catch (error) { try { - await this._processErrorIteratorMode(error); - } catch (error) { - try { - await this.close(); - } catch { - // We are not concerned with errors from close() - } - throw error; + await this.close(); + } catch { + // We are not concerned with errors from close() } + throw error; } } - }, callback); + } } /** * Try to get the next available document from the Change Stream's cursor or `null` if an empty batch is returned */ - tryNext(): Promise; - /** @deprecated Callbacks are deprecated and will be removed in the next major version. See [mongodb-legacy](https://github.com/mongodb-js/nodejs-mongodb-legacy) for migration assistance */ - tryNext(callback: Callback): void; - tryNext(callback?: Callback): Promise | void { + async tryNext(): Promise { this._setIsIterator(); - return maybeCallback(async () => { - // Change streams must resume indefinitely while each resume event succeeds. - // This loop continues until either a change event is received or until a resume attempt - // fails. - // eslint-disable-next-line no-constant-condition - while (true) { + // Change streams must resume indefinitely while each resume event succeeds. + // This loop continues until either a change event is received or until a resume attempt + // fails. + // eslint-disable-next-line no-constant-condition + while (true) { + try { + const change = await this.cursor.tryNext(); + return change ?? null; + } catch (error) { try { - const change = await this.cursor.tryNext(); - return change ?? null; + await this._processErrorIteratorMode(error); } catch (error) { try { - await this._processErrorIteratorMode(error); - } catch (error) { - try { - await this.close(); - } catch { - // We are not concerned with errors from close() - } - throw error; + await this.close(); + } catch { + // We are not concerned with errors from close() } + throw error; } } - }, callback); + } } async *[Symbol.asyncIterator](): AsyncGenerator { @@ -758,20 +743,15 @@ export class ChangeStream< } /** Close the Change Stream */ - close(): Promise; - /** @deprecated Callbacks are deprecated and will be removed in the next major version. See [mongodb-legacy](https://github.com/mongodb-js/nodejs-mongodb-legacy) for migration assistance */ - close(callback: Callback): void; - close(callback?: Callback): Promise | void { + async close(): Promise { this[kClosed] = true; - return maybeCallback(async () => { - const cursor = this.cursor; - try { - await cursor.close(); - } finally { - this._endStream(); - } - }, callback); + const cursor = this.cursor; + try { + await cursor.close(); + } finally { + this._endStream(); + } } /** @@ -864,9 +844,7 @@ export class ChangeStream< private _closeEmitterModeWithError(error: AnyError): void { this.emit(ChangeStream.ERROR, error); - this.close(() => { - // nothing to do - }); + this.close().catch(() => null); } /** @internal */ From 28d20939551ae379f6fd2cfc2e16bdbb5cc82f57 Mon Sep 17 00:00:00 2001 From: Neal Beeken Date: Tue, 20 Dec 2022 11:56:30 -0500 Subject: [PATCH 07/34] feat: remove callbacks from collection.ts --- src/collection.ts | 796 ++++++---------------------------------------- 1 file changed, 103 insertions(+), 693 deletions(-) diff --git a/src/collection.ts b/src/collection.ts index 7c6a37e6df1..a530e744cd2 100644 --- a/src/collection.ts +++ b/src/collection.ts @@ -83,7 +83,6 @@ import { import { ReadConcern, ReadConcernLike } from './read_concern'; import { ReadPreference, ReadPreferenceLike } from './read_preference'; import { - Callback, checkCollectionName, DEFAULT_PK_FACTORY, MongoDBNamespace, @@ -254,32 +253,15 @@ export class Collection { * * @param doc - The document to insert * @param options - Optional settings for the command - * @param callback - An optional callback, a Promise will be returned if none is provided */ - insertOne(doc: OptionalUnlessRequiredId): Promise>; - insertOne( + async insertOne( doc: OptionalUnlessRequiredId, - options: InsertOneOptions - ): Promise>; - /** @deprecated Callbacks are deprecated and will be removed in the next major version. See [mongodb-legacy](https://github.com/mongodb-js/nodejs-mongodb-legacy) for migration assistance */ - insertOne( - doc: OptionalUnlessRequiredId, - callback: Callback> - ): void; - /** @deprecated Callbacks are deprecated and will be removed in the next major version. See [mongodb-legacy](https://github.com/mongodb-js/nodejs-mongodb-legacy) for migration assistance */ - insertOne( - doc: OptionalUnlessRequiredId, - options: InsertOneOptions, - callback: Callback> - ): void; - insertOne( - doc: OptionalUnlessRequiredId, - options?: InsertOneOptions | Callback>, - callback?: Callback> - ): Promise> | void { - if (typeof options === 'function') { - callback = options; - options = {}; + options?: InsertOneOptions + ): Promise> { + // TODO(NODE-4751): versions of mongodb-client-encryption before v1.2.6 pass in hardcoded { w: 'majority' } + // specifically to an insertOne call in createDataKey, so we want to support this only here + if (options && Reflect.get(options, 'w')) { + options.writeConcern = WriteConcern.fromOptions(Reflect.get(options, 'w')); } return executeOperation( @@ -288,8 +270,7 @@ export class Collection { this as TODO_NODE_3286, doc, resolveOptions(this, options) - ) as TODO_NODE_3286, - callback + ) as TODO_NODE_3286 ); } @@ -300,40 +281,18 @@ export class Collection { * * @param docs - The documents to insert * @param options - Optional settings for the command - * @param callback - An optional callback, a Promise will be returned if none is provided */ - insertMany(docs: OptionalUnlessRequiredId[]): Promise>; - insertMany( + async insertMany( docs: OptionalUnlessRequiredId[], - options: BulkWriteOptions - ): Promise>; - /** @deprecated Callbacks are deprecated and will be removed in the next major version. See [mongodb-legacy](https://github.com/mongodb-js/nodejs-mongodb-legacy) for migration assistance */ - insertMany( - docs: OptionalUnlessRequiredId[], - callback: Callback> - ): void; - /** @deprecated Callbacks are deprecated and will be removed in the next major version. See [mongodb-legacy](https://github.com/mongodb-js/nodejs-mongodb-legacy) for migration assistance */ - insertMany( - docs: OptionalUnlessRequiredId[], - options: BulkWriteOptions, - callback: Callback> - ): void; - insertMany( - docs: OptionalUnlessRequiredId[], - options?: BulkWriteOptions | Callback>, - callback?: Callback> - ): Promise> | void { - if (typeof options === 'function') (callback = options), (options = {}); - options = options ? Object.assign({}, options) : { ordered: true }; - + options?: BulkWriteOptions + ): Promise> { return executeOperation( this.s.db.s.client, new InsertManyOperation( this as TODO_NODE_3286, docs, - resolveOptions(this, options) - ) as TODO_NODE_3286, - callback + resolveOptions(this, options ?? { ordered: true }) + ) as TODO_NODE_3286 ); } @@ -348,41 +307,18 @@ export class Collection { * - `deleteOne` * - `deleteMany` * - * Please note that raw operations are no longer accepted as of driver version 4.0. - * * If documents passed in do not contain the **_id** field, * one will be added to each of the documents missing it by the driver, mutating the document. This behavior * can be overridden by setting the **forceServerObjectId** flag. * * @param operations - Bulk operations to perform * @param options - Optional settings for the command - * @param callback - An optional callback, a Promise will be returned if none is provided * @throws MongoDriverError if operations is not an array */ - bulkWrite(operations: AnyBulkWriteOperation[]): Promise; - bulkWrite( - operations: AnyBulkWriteOperation[], - options: BulkWriteOptions - ): Promise; - /** @deprecated Callbacks are deprecated and will be removed in the next major version. See [mongodb-legacy](https://github.com/mongodb-js/nodejs-mongodb-legacy) for migration assistance */ - bulkWrite( - operations: AnyBulkWriteOperation[], - callback: Callback - ): void; - /** @deprecated Callbacks are deprecated and will be removed in the next major version. See [mongodb-legacy](https://github.com/mongodb-js/nodejs-mongodb-legacy) for migration assistance */ - bulkWrite( + async bulkWrite( operations: AnyBulkWriteOperation[], - options: BulkWriteOptions, - callback: Callback - ): void; - bulkWrite( - operations: AnyBulkWriteOperation[], - options?: BulkWriteOptions | Callback, - callback?: Callback - ): Promise | void { - if (typeof options === 'function') (callback = options), (options = {}); - options = options || { ordered: true }; - + options?: BulkWriteOptions + ): Promise { if (!Array.isArray(operations)) { throw new MongoInvalidArgumentError('Argument "operations" must be an array of documents'); } @@ -392,9 +328,8 @@ export class Collection { new BulkWriteOperation( this as TODO_NODE_3286, operations as TODO_NODE_3286, - resolveOptions(this, options) - ), - callback + resolveOptions(this, options ?? { ordered: true }) + ) ); } @@ -404,38 +339,12 @@ export class Collection { * @param filter - The filter used to select the document to update * @param update - The update operations to be applied to the document * @param options - Optional settings for the command - * @param callback - An optional callback, a Promise will be returned if none is provided */ - updateOne( - filter: Filter, - update: UpdateFilter | Partial - ): Promise; - updateOne( - filter: Filter, - update: UpdateFilter | Partial, - options: UpdateOptions - ): Promise; - /** @deprecated Callbacks are deprecated and will be removed in the next major version. See [mongodb-legacy](https://github.com/mongodb-js/nodejs-mongodb-legacy) for migration assistance */ - updateOne( - filter: Filter, - update: UpdateFilter | Partial, - callback: Callback - ): void; - /** @deprecated Callbacks are deprecated and will be removed in the next major version. See [mongodb-legacy](https://github.com/mongodb-js/nodejs-mongodb-legacy) for migration assistance */ - updateOne( - filter: Filter, - update: UpdateFilter | Partial, - options: UpdateOptions, - callback: Callback - ): void; - updateOne( + async updateOne( filter: Filter, update: UpdateFilter | Partial, - options?: UpdateOptions | Callback, - callback?: Callback - ): Promise | void { - if (typeof options === 'function') (callback = options), (options = {}); - + options?: UpdateOptions + ): Promise { return executeOperation( this.s.db.s.client, new UpdateOneOperation( @@ -443,8 +352,7 @@ export class Collection { filter, update, resolveOptions(this, options) - ) as TODO_NODE_3286, - callback + ) as TODO_NODE_3286 ); } @@ -454,38 +362,12 @@ export class Collection { * @param filter - The filter used to select the document to replace * @param replacement - The Document that replaces the matching document * @param options - Optional settings for the command - * @param callback - An optional callback, a Promise will be returned if none is provided */ - replaceOne( - filter: Filter, - replacement: WithoutId - ): Promise; - replaceOne( - filter: Filter, - replacement: WithoutId, - options: ReplaceOptions - ): Promise; - /** @deprecated Callbacks are deprecated and will be removed in the next major version. See [mongodb-legacy](https://github.com/mongodb-js/nodejs-mongodb-legacy) for migration assistance */ - replaceOne( - filter: Filter, - replacement: WithoutId, - callback: Callback - ): void; - /** @deprecated Callbacks are deprecated and will be removed in the next major version. See [mongodb-legacy](https://github.com/mongodb-js/nodejs-mongodb-legacy) for migration assistance */ - replaceOne( - filter: Filter, - replacement: WithoutId, - options: ReplaceOptions, - callback: Callback - ): void; - replaceOne( + async replaceOne( filter: Filter, replacement: WithoutId, - options?: ReplaceOptions | Callback, - callback?: Callback - ): Promise | void { - if (typeof options === 'function') (callback = options), (options = {}); - + options?: ReplaceOptions + ): Promise { return executeOperation( this.s.db.s.client, new ReplaceOneOperation( @@ -493,8 +375,7 @@ export class Collection { filter, replacement, resolveOptions(this, options) - ), - callback + ) ); } @@ -504,38 +385,12 @@ export class Collection { * @param filter - The filter used to select the documents to update * @param update - The update operations to be applied to the documents * @param options - Optional settings for the command - * @param callback - An optional callback, a Promise will be returned if none is provided */ - updateMany( - filter: Filter, - update: UpdateFilter - ): Promise; - updateMany( - filter: Filter, - update: UpdateFilter, - options: UpdateOptions - ): Promise; - /** @deprecated Callbacks are deprecated and will be removed in the next major version. See [mongodb-legacy](https://github.com/mongodb-js/nodejs-mongodb-legacy) for migration assistance */ - updateMany( - filter: Filter, - update: UpdateFilter, - callback: Callback - ): void; - /** @deprecated Callbacks are deprecated and will be removed in the next major version. See [mongodb-legacy](https://github.com/mongodb-js/nodejs-mongodb-legacy) for migration assistance */ - updateMany( - filter: Filter, - update: UpdateFilter, - options: UpdateOptions, - callback: Callback - ): void; - updateMany( + async updateMany( filter: Filter, update: UpdateFilter, - options?: UpdateOptions | Callback, - callback?: Callback - ): Promise | void { - if (typeof options === 'function') (callback = options), (options = {}); - + options?: UpdateOptions + ): Promise { return executeOperation( this.s.db.s.client, new UpdateManyOperation( @@ -543,8 +398,7 @@ export class Collection { filter, update, resolveOptions(this, options) - ), - callback + ) as TODO_NODE_3286 ); } @@ -553,29 +407,11 @@ export class Collection { * * @param filter - The filter used to select the document to remove * @param options - Optional settings for the command - * @param callback - An optional callback, a Promise will be returned if none is provided */ - deleteOne(filter: Filter): Promise; - deleteOne(filter: Filter, options: DeleteOptions): Promise; - /** @deprecated Callbacks are deprecated and will be removed in the next major version. See [mongodb-legacy](https://github.com/mongodb-js/nodejs-mongodb-legacy) for migration assistance */ - deleteOne(filter: Filter, callback: Callback): void; - /** @deprecated Callbacks are deprecated and will be removed in the next major version. See [mongodb-legacy](https://github.com/mongodb-js/nodejs-mongodb-legacy) for migration assistance */ - deleteOne( - filter: Filter, - options: DeleteOptions, - callback?: Callback - ): void; - deleteOne( - filter: Filter, - options?: DeleteOptions | Callback, - callback?: Callback - ): Promise | void { - if (typeof options === 'function') (callback = options), (options = {}); - + async deleteOne(filter: Filter, options?: DeleteOptions): Promise { return executeOperation( this.s.db.s.client, - new DeleteOneOperation(this as TODO_NODE_3286, filter, resolveOptions(this, options)), - callback + new DeleteOneOperation(this as TODO_NODE_3286, filter, resolveOptions(this, options)) ); } @@ -584,40 +420,11 @@ export class Collection { * * @param filter - The filter used to select the documents to remove * @param options - Optional settings for the command - * @param callback - An optional callback, a Promise will be returned if none is provided */ - deleteMany(filter: Filter): Promise; - deleteMany(filter: Filter, options: DeleteOptions): Promise; - /** @deprecated Callbacks are deprecated and will be removed in the next major version. See [mongodb-legacy](https://github.com/mongodb-js/nodejs-mongodb-legacy) for migration assistance */ - deleteMany(filter: Filter, callback: Callback): void; - /** @deprecated Callbacks are deprecated and will be removed in the next major version. See [mongodb-legacy](https://github.com/mongodb-js/nodejs-mongodb-legacy) for migration assistance */ - deleteMany( - filter: Filter, - options: DeleteOptions, - callback: Callback - ): void; - deleteMany( - filter: Filter, - options?: DeleteOptions | Callback, - callback?: Callback - ): Promise | void { - if (filter == null) { - filter = {}; - options = {}; - callback = undefined; - } else if (typeof filter === 'function') { - callback = filter as Callback; - filter = {}; - options = {}; - } else if (typeof options === 'function') { - callback = options; - options = {}; - } - + async deleteMany(filter: Filter, options?: DeleteOptions): Promise { return executeOperation( this.s.db.s.client, - new DeleteManyOperation(this as TODO_NODE_3286, filter, resolveOptions(this, options)), - callback + new DeleteManyOperation(this as TODO_NODE_3286, filter, resolveOptions(this, options)) ); } @@ -629,29 +436,15 @@ export class Collection { * * @param newName - New name of of the collection. * @param options - Optional settings for the command - * @param callback - An optional callback, a Promise will be returned if none is provided */ - rename(newName: string): Promise; - rename(newName: string, options: RenameOptions): Promise; - /** @deprecated Callbacks are deprecated and will be removed in the next major version. See [mongodb-legacy](https://github.com/mongodb-js/nodejs-mongodb-legacy) for migration assistance */ - rename(newName: string, callback: Callback): void; - /** @deprecated Callbacks are deprecated and will be removed in the next major version. See [mongodb-legacy](https://github.com/mongodb-js/nodejs-mongodb-legacy) for migration assistance */ - rename(newName: string, options: RenameOptions, callback: Callback): void; - rename( - newName: string, - options?: RenameOptions | Callback, - callback?: Callback - ): Promise | void { - if (typeof options === 'function') (callback = options), (options = {}); - + async rename(newName: string, options?: RenameOptions): Promise { // Intentionally, we do not inherit options from parent for this operation. return executeOperation( this.s.db.s.client, new RenameOperation(this as TODO_NODE_3286, newName, { ...options, readPreference: ReadPreference.PRIMARY - }) as TODO_NODE_3286, - callback + }) as TODO_NODE_3286 ); } @@ -659,25 +452,11 @@ export class Collection { * Drop the collection from the database, removing it permanently. New accesses will create a new collection. * * @param options - Optional settings for the command - * @param callback - An optional callback, a Promise will be returned if none is provided */ - drop(): Promise; - drop(options: DropCollectionOptions): Promise; - /** @deprecated Callbacks are deprecated and will be removed in the next major version. See [mongodb-legacy](https://github.com/mongodb-js/nodejs-mongodb-legacy) for migration assistance */ - drop(callback: Callback): void; - /** @deprecated Callbacks are deprecated and will be removed in the next major version. See [mongodb-legacy](https://github.com/mongodb-js/nodejs-mongodb-legacy) for migration assistance */ - drop(options: DropCollectionOptions, callback: Callback): void; - drop( - options?: DropCollectionOptions | Callback, - callback?: Callback - ): Promise | void { - if (typeof options === 'function') (callback = options), (options = {}); - options = options ?? {}; - + async drop(options?: DropCollectionOptions): Promise { return executeOperation( this.s.db.s.client, - new DropCollectionOperation(this.s.db, this.collectionName, options), - callback + new DropCollectionOperation(this.s.db, this.collectionName, options) ); } @@ -686,59 +465,21 @@ export class Collection { * * @param filter - Query for find Operation * @param options - Optional settings for the command - * @param callback - An optional callback, a Promise will be returned if none is provided */ - findOne(): Promise | null>; - findOne(filter: Filter): Promise | null>; - findOne(filter: Filter, options: FindOptions): Promise | null>; - /** @deprecated Callbacks are deprecated and will be removed in the next major version. See [mongodb-legacy](https://github.com/mongodb-js/nodejs-mongodb-legacy) for migration assistance */ - findOne(callback: Callback | null>): void; - /** @deprecated Callbacks are deprecated and will be removed in the next major version. See [mongodb-legacy](https://github.com/mongodb-js/nodejs-mongodb-legacy) for migration assistance */ - findOne(filter: Filter, callback: Callback | null>): void; - /** @deprecated Callbacks are deprecated and will be removed in the next major version. See [mongodb-legacy](https://github.com/mongodb-js/nodejs-mongodb-legacy) for migration assistance */ - findOne( - filter: Filter, - options: FindOptions, - callback: Callback | null> - ): void; + async findOne(): Promise | null>; + async findOne(filter: Filter): Promise | null>; + async findOne(filter: Filter, options: FindOptions): Promise | null>; // allow an override of the schema. - findOne(): Promise; - findOne(filter: Filter): Promise; - findOne(filter: Filter, options?: FindOptions): Promise; - /** @deprecated Callbacks are deprecated and will be removed in the next major version. See [mongodb-legacy](https://github.com/mongodb-js/nodejs-mongodb-legacy) for migration assistance */ - findOne(callback: Callback): void; - /** @deprecated Callbacks are deprecated and will be removed in the next major version. See [mongodb-legacy](https://github.com/mongodb-js/nodejs-mongodb-legacy) for migration assistance */ - findOne( - filter: Filter, - options?: FindOptions, - callback?: Callback - ): void; + async findOne(): Promise; + async findOne(filter: Filter): Promise; + async findOne(filter: Filter, options?: FindOptions): Promise; - findOne( - filter?: Filter | Callback | null>, - options?: FindOptions | Callback | null>, - callback?: Callback | null> - ): Promise | null> | void { - if (callback != null && typeof callback !== 'function') { - throw new MongoInvalidArgumentError( - 'Third parameter to `findOne()` must be a callback or undefined' - ); - } - - if (typeof filter === 'function') { - callback = filter; - filter = {}; - options = {}; - } - if (typeof options === 'function') { - callback = options; - options = {}; - } - - const finalFilter = filter ?? {}; - const finalOptions = options ?? {}; - return this.find(finalFilter, finalOptions).limit(-1).batchSize(1).next(callback); + async findOne(filter?: Filter, options?: FindOptions): Promise | null> { + return this.find(filter ?? {}, options ?? {}) + .limit(-1) + .batchSize(1) + .next(); } /** @@ -771,24 +512,11 @@ export class Collection { * Returns the options of the collection. * * @param options - Optional settings for the command - * @param callback - An optional callback, a Promise will be returned if none is provided */ - options(): Promise; - options(options: OperationOptions): Promise; - /** @deprecated Callbacks are deprecated and will be removed in the next major version. See [mongodb-legacy](https://github.com/mongodb-js/nodejs-mongodb-legacy) for migration assistance */ - options(callback: Callback): void; - /** @deprecated Callbacks are deprecated and will be removed in the next major version. See [mongodb-legacy](https://github.com/mongodb-js/nodejs-mongodb-legacy) for migration assistance */ - options(options: OperationOptions, callback: Callback): void; - options( - options?: OperationOptions | Callback, - callback?: Callback - ): Promise | void { - if (typeof options === 'function') (callback = options), (options = {}); - + async options(options?: OperationOptions): Promise { return executeOperation( this.s.db.s.client, - new OptionsOperation(this as TODO_NODE_3286, resolveOptions(this, options)), - callback + new OptionsOperation(this as TODO_NODE_3286, resolveOptions(this, options)) ); } @@ -796,24 +524,11 @@ export class Collection { * Returns if the collection is a capped collection * * @param options - Optional settings for the command - * @param callback - An optional callback, a Promise will be returned if none is provided */ - isCapped(): Promise; - isCapped(options: OperationOptions): Promise; - /** @deprecated Callbacks are deprecated and will be removed in the next major version. See [mongodb-legacy](https://github.com/mongodb-js/nodejs-mongodb-legacy) for migration assistance */ - isCapped(callback: Callback): void; - /** @deprecated Callbacks are deprecated and will be removed in the next major version. See [mongodb-legacy](https://github.com/mongodb-js/nodejs-mongodb-legacy) for migration assistance */ - isCapped(options: OperationOptions, callback: Callback): void; - isCapped( - options?: OperationOptions | Callback, - callback?: Callback - ): Promise | void { - if (typeof options === 'function') (callback = options), (options = {}); - + async isCapped(options?: OperationOptions): Promise { return executeOperation( this.s.db.s.client, - new IsCappedOperation(this as TODO_NODE_3286, resolveOptions(this, options)), - callback + new IsCappedOperation(this as TODO_NODE_3286, resolveOptions(this, options)) ); } @@ -822,7 +537,6 @@ export class Collection { * * @param indexSpec - The field name or index specification to create an index for * @param options - Optional settings for the command - * @param callback - An optional callback, a Promise will be returned if none is provided * * @example * ```ts @@ -846,23 +560,10 @@ export class Collection { * await collection.createIndex(['j', ['k', -1], { l: '2d' }]) * ``` */ - createIndex(indexSpec: IndexSpecification): Promise; - createIndex(indexSpec: IndexSpecification, options: CreateIndexesOptions): Promise; - /** @deprecated Callbacks are deprecated and will be removed in the next major version. See [mongodb-legacy](https://github.com/mongodb-js/nodejs-mongodb-legacy) for migration assistance */ - createIndex(indexSpec: IndexSpecification, callback: Callback): void; - /** @deprecated Callbacks are deprecated and will be removed in the next major version. See [mongodb-legacy](https://github.com/mongodb-js/nodejs-mongodb-legacy) for migration assistance */ - createIndex( + async createIndex( indexSpec: IndexSpecification, - options: CreateIndexesOptions, - callback: Callback - ): void; - createIndex( - indexSpec: IndexSpecification, - options?: CreateIndexesOptions | Callback, - callback?: Callback - ): Promise | void { - if (typeof options === 'function') (callback = options), (options = {}); - + options?: CreateIndexesOptions + ): Promise { return executeOperation( this.s.db.s.client, new CreateIndexOperation( @@ -870,8 +571,7 @@ export class Collection { this.collectionName, indexSpec, resolveOptions(this, options) - ), - callback + ) ); } @@ -885,7 +585,6 @@ export class Collection { * * @param indexSpecs - An array of index specifications to be created * @param options - Optional settings for the command - * @param callback - An optional callback, a Promise will be returned if none is provided * * @example * ```ts @@ -907,34 +606,18 @@ export class Collection { * ]); * ``` */ - createIndexes(indexSpecs: IndexDescription[]): Promise; - createIndexes(indexSpecs: IndexDescription[], options: CreateIndexesOptions): Promise; - /** @deprecated Callbacks are deprecated and will be removed in the next major version. See [mongodb-legacy](https://github.com/mongodb-js/nodejs-mongodb-legacy) for migration assistance */ - createIndexes(indexSpecs: IndexDescription[], callback: Callback): void; - /** @deprecated Callbacks are deprecated and will be removed in the next major version. See [mongodb-legacy](https://github.com/mongodb-js/nodejs-mongodb-legacy) for migration assistance */ - createIndexes( + async createIndexes( indexSpecs: IndexDescription[], - options: CreateIndexesOptions, - callback: Callback - ): void; - createIndexes( - indexSpecs: IndexDescription[], - options?: CreateIndexesOptions | Callback, - callback?: Callback - ): Promise | void { - if (typeof options === 'function') (callback = options), (options = {}); - options = options ? Object.assign({}, options) : {}; - if (typeof options.maxTimeMS !== 'number') delete options.maxTimeMS; - + options?: CreateIndexesOptions + ): Promise { return executeOperation( this.s.db.s.client, new CreateIndexesOperation( this as TODO_NODE_3286, this.collectionName, indexSpecs, - resolveOptions(this, options) - ), - callback + resolveOptions(this, { ...options, maxTimeMS: undefined }) + ) ); } @@ -943,29 +626,14 @@ export class Collection { * * @param indexName - Name of the index to drop. * @param options - Optional settings for the command - * @param callback - An optional callback, a Promise will be returned if none is provided */ - dropIndex(indexName: string): Promise; - dropIndex(indexName: string, options: DropIndexesOptions): Promise; - /** @deprecated Callbacks are deprecated and will be removed in the next major version. See [mongodb-legacy](https://github.com/mongodb-js/nodejs-mongodb-legacy) for migration assistance */ - dropIndex(indexName: string, callback: Callback): void; - /** @deprecated Callbacks are deprecated and will be removed in the next major version. See [mongodb-legacy](https://github.com/mongodb-js/nodejs-mongodb-legacy) for migration assistance */ - dropIndex(indexName: string, options: DropIndexesOptions, callback: Callback): void; - dropIndex( - indexName: string, - options?: DropIndexesOptions | Callback, - callback?: Callback - ): Promise | void { - if (typeof options === 'function') (callback = options), (options = {}); - options = resolveOptions(this, options); - - // Run only against primary - options.readPreference = ReadPreference.primary; - + async dropIndex(indexName: string, options?: DropIndexesOptions): Promise { return executeOperation( this.s.db.s.client, - new DropIndexOperation(this as TODO_NODE_3286, indexName, options), - callback + new DropIndexOperation(this as TODO_NODE_3286, indexName, { + ...resolveOptions(this, options), + readPreference: ReadPreference.primary + }) ); } @@ -973,24 +641,11 @@ export class Collection { * Drops all indexes from this collection. * * @param options - Optional settings for the command - * @param callback - An optional callback, a Promise will be returned if none is provided */ - dropIndexes(): Promise; - dropIndexes(options: DropIndexesOptions): Promise; - /** @deprecated Callbacks are deprecated and will be removed in the next major version. See [mongodb-legacy](https://github.com/mongodb-js/nodejs-mongodb-legacy) for migration assistance */ - dropIndexes(callback: Callback): void; - /** @deprecated Callbacks are deprecated and will be removed in the next major version. See [mongodb-legacy](https://github.com/mongodb-js/nodejs-mongodb-legacy) for migration assistance */ - dropIndexes(options: DropIndexesOptions, callback: Callback): void; - dropIndexes( - options?: DropIndexesOptions | Callback, - callback?: Callback - ): Promise | void { - if (typeof options === 'function') (callback = options), (options = {}); - + async dropIndexes(options?: DropIndexesOptions): Promise { return executeOperation( this.s.db.s.client, - new DropIndexesOperation(this as TODO_NODE_3286, resolveOptions(this, options)), - callback + new DropIndexesOperation(this as TODO_NODE_3286, resolveOptions(this, options)) ); } @@ -1008,29 +663,14 @@ export class Collection { * * @param indexes - One or more index names to check. * @param options - Optional settings for the command - * @param callback - An optional callback, a Promise will be returned if none is provided */ - indexExists(indexes: string | string[]): Promise; - indexExists(indexes: string | string[], options: IndexInformationOptions): Promise; - /** @deprecated Callbacks are deprecated and will be removed in the next major version. See [mongodb-legacy](https://github.com/mongodb-js/nodejs-mongodb-legacy) for migration assistance */ - indexExists(indexes: string | string[], callback: Callback): void; - /** @deprecated Callbacks are deprecated and will be removed in the next major version. See [mongodb-legacy](https://github.com/mongodb-js/nodejs-mongodb-legacy) for migration assistance */ - indexExists( + async indexExists( indexes: string | string[], - options: IndexInformationOptions, - callback: Callback - ): void; - indexExists( - indexes: string | string[], - options?: IndexInformationOptions | Callback, - callback?: Callback - ): Promise | void { - if (typeof options === 'function') (callback = options), (options = {}); - + options?: IndexInformationOptions + ): Promise { return executeOperation( this.s.db.s.client, - new IndexExistsOperation(this as TODO_NODE_3286, indexes, resolveOptions(this, options)), - callback + new IndexExistsOperation(this as TODO_NODE_3286, indexes, resolveOptions(this, options)) ); } @@ -1038,24 +678,11 @@ export class Collection { * Retrieves this collections index info. * * @param options - Optional settings for the command - * @param callback - An optional callback, a Promise will be returned if none is provided */ - indexInformation(): Promise; - indexInformation(options: IndexInformationOptions): Promise; - /** @deprecated Callbacks are deprecated and will be removed in the next major version. See [mongodb-legacy](https://github.com/mongodb-js/nodejs-mongodb-legacy) for migration assistance */ - indexInformation(callback: Callback): void; - /** @deprecated Callbacks are deprecated and will be removed in the next major version. See [mongodb-legacy](https://github.com/mongodb-js/nodejs-mongodb-legacy) for migration assistance */ - indexInformation(options: IndexInformationOptions, callback: Callback): void; - indexInformation( - options?: IndexInformationOptions | Callback, - callback?: Callback - ): Promise | void { - if (typeof options === 'function') (callback = options), (options = {}); - + async indexInformation(options?: IndexInformationOptions): Promise { return executeOperation( this.s.db.s.client, - new IndexInformationOperation(this.s.db, this.collectionName, resolveOptions(this, options)), - callback + new IndexInformationOperation(this.s.db, this.collectionName, resolveOptions(this, options)) ); } @@ -1071,23 +698,11 @@ export class Collection { * * @see {@link https://www.mongodb.com/docs/manual/reference/command/count/#behavior|Count: Behavior} * @param options - Optional settings for the command - * @param callback - An optional callback, a Promise will be returned if none is provided */ - estimatedDocumentCount(): Promise; - estimatedDocumentCount(options: EstimatedDocumentCountOptions): Promise; - /** @deprecated Callbacks are deprecated and will be removed in the next major version. See [mongodb-legacy](https://github.com/mongodb-js/nodejs-mongodb-legacy) for migration assistance */ - estimatedDocumentCount(callback: Callback): void; - /** @deprecated Callbacks are deprecated and will be removed in the next major version. See [mongodb-legacy](https://github.com/mongodb-js/nodejs-mongodb-legacy) for migration assistance */ - estimatedDocumentCount(options: EstimatedDocumentCountOptions, callback: Callback): void; - estimatedDocumentCount( - options?: EstimatedDocumentCountOptions | Callback, - callback?: Callback - ): Promise | void { - if (typeof options === 'function') (callback = options), (options = {}); + async estimatedDocumentCount(options?: EstimatedDocumentCountOptions): Promise { return executeOperation( this.s.db.s.client, - new EstimatedDocumentCountOperation(this as TODO_NODE_3286, resolveOptions(this, options)), - callback + new EstimatedDocumentCountOperation(this as TODO_NODE_3286, resolveOptions(this, options)) ); } @@ -1110,50 +725,17 @@ export class Collection { * * @param filter - The filter for the count * @param options - Optional settings for the command - * @param callback - An optional callback, a Promise will be returned if none is provided * * @see https://docs.mongodb.com/manual/reference/operator/query/expr/ * @see https://docs.mongodb.com/manual/reference/operator/query/geoWithin/ * @see https://docs.mongodb.com/manual/reference/operator/query/center/#op._S_center * @see https://docs.mongodb.com/manual/reference/operator/query/centerSphere/#op._S_centerSphere */ - countDocuments(): Promise; - countDocuments(filter: Filter): Promise; - countDocuments(filter: Filter, options: CountDocumentsOptions): Promise; - /** @deprecated Callbacks are deprecated and will be removed in the next major version. See [mongodb-legacy](https://github.com/mongodb-js/nodejs-mongodb-legacy) for migration assistance */ - countDocuments(callback: Callback): void; - /** @deprecated Callbacks are deprecated and will be removed in the next major version. See [mongodb-legacy](https://github.com/mongodb-js/nodejs-mongodb-legacy) for migration assistance */ - countDocuments(filter: Filter, callback: Callback): void; - /** @deprecated Callbacks are deprecated and will be removed in the next major version. See [mongodb-legacy](https://github.com/mongodb-js/nodejs-mongodb-legacy) for migration assistance */ - countDocuments( - filter: Filter, - options: CountDocumentsOptions, - callback: Callback - ): void; - countDocuments( - filter?: Document | CountDocumentsOptions | Callback, - options?: CountDocumentsOptions | Callback, - callback?: Callback - ): Promise | void { - if (filter == null) { - (filter = {}), (options = {}), (callback = undefined); - } else if (typeof filter === 'function') { - (callback = filter as Callback), (filter = {}), (options = {}); - } else { - if (arguments.length === 2) { - if (typeof options === 'function') (callback = options), (options = {}); - } - } - + async countDocuments(filter?: Document, options?: CountDocumentsOptions): Promise { filter ??= {}; return executeOperation( this.s.db.s.client, - new CountDocumentsOperation( - this as TODO_NODE_3286, - filter, - resolveOptions(this, options as CountDocumentsOptions) - ), - callback + new CountDocumentsOperation(this as TODO_NODE_3286, filter, resolveOptions(this, options)) ); } @@ -1163,7 +745,6 @@ export class Collection { * @param key - Field of the document to find distinct values for * @param filter - The filter for filtering the set of documents to which we apply the distinct filter. * @param options - Optional settings for the command - * @param callback - An optional callback, a Promise will be returned if none is provided */ distinct>( key: Key @@ -1177,55 +758,17 @@ export class Collection { filter: Filter, options: DistinctOptions ): Promise[Key]>>>; - /** @deprecated Callbacks are deprecated and will be removed in the next major version. See [mongodb-legacy](https://github.com/mongodb-js/nodejs-mongodb-legacy) for migration assistance */ - distinct>( - key: Key, - callback: Callback[Key]>>> - ): void; - /** @deprecated Callbacks are deprecated and will be removed in the next major version. See [mongodb-legacy](https://github.com/mongodb-js/nodejs-mongodb-legacy) for migration assistance */ - distinct>( - key: Key, - filter: Filter, - callback: Callback[Key]>>> - ): void; - /** @deprecated Callbacks are deprecated and will be removed in the next major version. See [mongodb-legacy](https://github.com/mongodb-js/nodejs-mongodb-legacy) for migration assistance */ - distinct>( - key: Key, - filter: Filter, - options: DistinctOptions, - callback: Callback[Key]>>> - ): void; // Embedded documents overload distinct(key: string): Promise; distinct(key: string, filter: Filter): Promise; distinct(key: string, filter: Filter, options: DistinctOptions): Promise; - /** @deprecated Callbacks are deprecated and will be removed in the next major version. See [mongodb-legacy](https://github.com/mongodb-js/nodejs-mongodb-legacy) for migration assistance */ - distinct(key: string, callback: Callback): void; - /** @deprecated Callbacks are deprecated and will be removed in the next major version. See [mongodb-legacy](https://github.com/mongodb-js/nodejs-mongodb-legacy) for migration assistance */ - distinct(key: string, filter: Filter, callback: Callback): void; - /** @deprecated Callbacks are deprecated and will be removed in the next major version. See [mongodb-legacy](https://github.com/mongodb-js/nodejs-mongodb-legacy) for migration assistance */ - distinct( - key: string, - filter: Filter, - options: DistinctOptions, - callback: Callback - ): void; - // Implementation - distinct>( - key: Key, - filter?: Filter | DistinctOptions | Callback, - options?: DistinctOptions | Callback, - callback?: Callback - ): Promise | void { - if (typeof filter === 'function') { - (callback = filter), (filter = {}), (options = {}); - } else { - if (arguments.length === 3 && typeof options === 'function') { - (callback = options), (options = {}); - } - } + async distinct>( + key: Key, + filter?: Filter, + options?: DistinctOptions + ): Promise { filter ??= {}; return executeOperation( this.s.db.s.client, @@ -1234,8 +777,7 @@ export class Collection { key as TODO_NODE_3286, filter, resolveOptions(this, options as DistinctOptions) - ), - callback + ) ); } @@ -1243,24 +785,11 @@ export class Collection { * Retrieve all the indexes on the collection. * * @param options - Optional settings for the command - * @param callback - An optional callback, a Promise will be returned if none is provided */ - indexes(): Promise; - indexes(options: IndexInformationOptions): Promise; - /** @deprecated Callbacks are deprecated and will be removed in the next major version. See [mongodb-legacy](https://github.com/mongodb-js/nodejs-mongodb-legacy) for migration assistance */ - indexes(callback: Callback): void; - /** @deprecated Callbacks are deprecated and will be removed in the next major version. See [mongodb-legacy](https://github.com/mongodb-js/nodejs-mongodb-legacy) for migration assistance */ - indexes(options: IndexInformationOptions, callback: Callback): void; - indexes( - options?: IndexInformationOptions | Callback, - callback?: Callback - ): Promise | void { - if (typeof options === 'function') (callback = options), (options = {}); - + async indexes(options?: IndexInformationOptions): Promise { return executeOperation( this.s.db.s.client, - new IndexesOperation(this as TODO_NODE_3286, resolveOptions(this, options)), - callback + new IndexesOperation(this as TODO_NODE_3286, resolveOptions(this, options)) ); } @@ -1268,25 +797,11 @@ export class Collection { * Get all the collection statistics. * * @param options - Optional settings for the command - * @param callback - An optional callback, a Promise will be returned if none is provided */ - stats(): Promise; - stats(options: CollStatsOptions): Promise; - /** @deprecated Callbacks are deprecated and will be removed in the next major version. See [mongodb-legacy](https://github.com/mongodb-js/nodejs-mongodb-legacy) for migration assistance */ - stats(callback: Callback): void; - /** @deprecated Callbacks are deprecated and will be removed in the next major version. See [mongodb-legacy](https://github.com/mongodb-js/nodejs-mongodb-legacy) for migration assistance */ - stats(options: CollStatsOptions, callback: Callback): void; - stats( - options?: CollStatsOptions | Callback, - callback?: Callback - ): Promise | void { - if (typeof options === 'function') (callback = options), (options = {}); - options = options ?? {}; - + async stats(options?: CollStatsOptions): Promise { return executeOperation( this.s.db.s.client, - new CollStatsOperation(this as TODO_NODE_3286, options) as TODO_NODE_3286, - callback + new CollStatsOperation(this as TODO_NODE_3286, options) as TODO_NODE_3286 ); } @@ -1295,36 +810,18 @@ export class Collection { * * @param filter - The filter used to select the document to remove * @param options - Optional settings for the command - * @param callback - An optional callback, a Promise will be returned if none is provided */ - findOneAndDelete(filter: Filter): Promise>; - findOneAndDelete( - filter: Filter, - options: FindOneAndDeleteOptions - ): Promise>; - /** @deprecated Callbacks are deprecated and will be removed in the next major version. See [mongodb-legacy](https://github.com/mongodb-js/nodejs-mongodb-legacy) for migration assistance */ - findOneAndDelete(filter: Filter, callback: Callback>): void; - /** @deprecated Callbacks are deprecated and will be removed in the next major version. See [mongodb-legacy](https://github.com/mongodb-js/nodejs-mongodb-legacy) for migration assistance */ - findOneAndDelete( + async findOneAndDelete( filter: Filter, - options: FindOneAndDeleteOptions, - callback: Callback> - ): void; - findOneAndDelete( - filter: Filter, - options?: FindOneAndDeleteOptions | Callback>, - callback?: Callback> - ): Promise | void { - if (typeof options === 'function') (callback = options), (options = {}); - + options?: FindOneAndDeleteOptions + ): Promise> { return executeOperation( this.s.db.s.client, new FindOneAndDeleteOperation( this as TODO_NODE_3286, filter, resolveOptions(this, options) - ) as TODO_NODE_3286, - callback + ) as TODO_NODE_3286 ); } @@ -1334,38 +831,12 @@ export class Collection { * @param filter - The filter used to select the document to replace * @param replacement - The Document that replaces the matching document * @param options - Optional settings for the command - * @param callback - An optional callback, a Promise will be returned if none is provided */ - findOneAndReplace( - filter: Filter, - replacement: WithoutId - ): Promise>; - findOneAndReplace( - filter: Filter, - replacement: WithoutId, - options: FindOneAndReplaceOptions - ): Promise>; - /** @deprecated Callbacks are deprecated and will be removed in the next major version. See [mongodb-legacy](https://github.com/mongodb-js/nodejs-mongodb-legacy) for migration assistance */ - findOneAndReplace( + async findOneAndReplace( filter: Filter, replacement: WithoutId, - callback: Callback> - ): void; - /** @deprecated Callbacks are deprecated and will be removed in the next major version. See [mongodb-legacy](https://github.com/mongodb-js/nodejs-mongodb-legacy) for migration assistance */ - findOneAndReplace( - filter: Filter, - replacement: WithoutId, - options: FindOneAndReplaceOptions, - callback: Callback> - ): void; - findOneAndReplace( - filter: Filter, - replacement: WithoutId, - options?: FindOneAndReplaceOptions | Callback>, - callback?: Callback> - ): Promise> | void { - if (typeof options === 'function') (callback = options), (options = {}); - + options?: FindOneAndReplaceOptions + ): Promise> { return executeOperation( this.s.db.s.client, new FindOneAndReplaceOperation( @@ -1373,8 +844,7 @@ export class Collection { filter, replacement, resolveOptions(this, options) - ) as TODO_NODE_3286, - callback + ) as TODO_NODE_3286 ); } @@ -1384,38 +854,12 @@ export class Collection { * @param filter - The filter used to select the document to update * @param update - Update operations to be performed on the document * @param options - Optional settings for the command - * @param callback - An optional callback, a Promise will be returned if none is provided */ - findOneAndUpdate( - filter: Filter, - update: UpdateFilter - ): Promise>; - findOneAndUpdate( - filter: Filter, - update: UpdateFilter, - options: FindOneAndUpdateOptions - ): Promise>; - /** @deprecated Callbacks are deprecated and will be removed in the next major version. See [mongodb-legacy](https://github.com/mongodb-js/nodejs-mongodb-legacy) for migration assistance */ - findOneAndUpdate( + async findOneAndUpdate( filter: Filter, update: UpdateFilter, - callback: Callback> - ): void; - /** @deprecated Callbacks are deprecated and will be removed in the next major version. See [mongodb-legacy](https://github.com/mongodb-js/nodejs-mongodb-legacy) for migration assistance */ - findOneAndUpdate( - filter: Filter, - update: UpdateFilter, - options: FindOneAndUpdateOptions, - callback: Callback> - ): void; - findOneAndUpdate( - filter: Filter, - update: UpdateFilter, - options?: FindOneAndUpdateOptions | Callback>, - callback?: Callback> - ): Promise> | void { - if (typeof options === 'function') (callback = options), (options = {}); - + options?: FindOneAndUpdateOptions + ): Promise> { return executeOperation( this.s.db.s.client, new FindOneAndUpdateOperation( @@ -1423,8 +867,7 @@ export class Collection { filter, update, resolveOptions(this, options) - ) as TODO_NODE_3286, - callback + ) as TODO_NODE_3286 ); } @@ -1438,19 +881,11 @@ export class Collection { pipeline: Document[] = [], options?: AggregateOptions ): AggregationCursor { - if (arguments.length > 2) { - throw new MongoInvalidArgumentError( - 'Method "collection.aggregate()" accepts at most two arguments' - ); - } if (!Array.isArray(pipeline)) { throw new MongoInvalidArgumentError( 'Argument "pipeline" must be an array of aggregation stages' ); } - if (typeof options === 'function') { - throw new MongoInvalidArgumentError('Argument "options" must not be function'); - } return new AggregationCursor( this.s.db.s.client, @@ -1555,32 +990,8 @@ export class Collection { * * @param filter - The filter for the count. * @param options - Optional settings for the command - * @param callback - An optional callback, a Promise will be returned if none is provided */ - count(): Promise; - count(filter: Filter): Promise; - count(filter: Filter, options: CountOptions): Promise; - /** @deprecated Callbacks are deprecated and will be removed in the next major version. See [mongodb-legacy](https://github.com/mongodb-js/nodejs-mongodb-legacy) for migration assistance */ - count(callback: Callback): void; - /** @deprecated Callbacks are deprecated and will be removed in the next major version. See [mongodb-legacy](https://github.com/mongodb-js/nodejs-mongodb-legacy) for migration assistance */ - count(filter: Filter, callback: Callback): void; - /** @deprecated Callbacks are deprecated and will be removed in the next major version. See [mongodb-legacy](https://github.com/mongodb-js/nodejs-mongodb-legacy) for migration assistance */ - count( - filter: Filter, - options: CountOptions, - callback: Callback - ): Promise | void; - count( - filter?: Filter | CountOptions | Callback, - options?: CountOptions | Callback, - callback?: Callback - ): Promise | void { - if (typeof filter === 'function') { - (callback = filter), (filter = {}), (options = {}); - } else { - if (typeof options === 'function') (callback = options), (options = {}); - } - + async count(filter?: Filter, options?: CountOptions): Promise { filter ??= {}; return executeOperation( this.s.db.s.client, @@ -1588,8 +999,7 @@ export class Collection { MongoDBNamespace.fromString(this.namespace), filter, resolveOptions(this, options) - ), - callback + ) ); } } From 16ee9781b972f7fd70996079f4e0c0e572793b62 Mon Sep 17 00:00:00 2001 From: Neal Beeken Date: Tue, 20 Dec 2022 12:27:24 -0500 Subject: [PATCH 08/34] feat: remove callbacks from cursors --- src/cursor/abstract_cursor.ts | 115 +++++++++++-------------------- src/cursor/aggregation_cursor.ts | 17 +---- src/cursor/find_cursor.ts | 39 ++--------- 3 files changed, 49 insertions(+), 122 deletions(-) diff --git a/src/cursor/abstract_cursor.ts b/src/cursor/abstract_cursor.ts index b5fff69b303..67dceec5e23 100644 --- a/src/cursor/abstract_cursor.ts +++ b/src/cursor/abstract_cursor.ts @@ -21,7 +21,7 @@ import { ReadConcern, ReadConcernLike } from '../read_concern'; import { ReadPreference, ReadPreferenceLike } from '../read_preference'; import type { Server } from '../sdam/server'; import { ClientSession, maybeClearPinnedConnection } from '../sessions'; -import { Callback, List, maybeCallback, MongoDBNamespace, ns } from '../utils'; +import { Callback, List, MongoDBNamespace, ns } from '../utils'; /** @internal */ const kId = Symbol('id'); @@ -352,60 +352,43 @@ export abstract class AbstractCursor< return new ReadableCursorStream(this); } - hasNext(): Promise; - /** @deprecated Callbacks are deprecated and will be removed in the next major version. See [mongodb-legacy](https://github.com/mongodb-js/nodejs-mongodb-legacy) for migration assistance */ - hasNext(callback: Callback): void; - hasNext(callback?: Callback): Promise | void { - return maybeCallback(async () => { - if (this[kId] === Long.ZERO) { - return false; - } + async hasNext(): Promise { + if (this[kId] === Long.ZERO) { + return false; + } - if (this[kDocuments].length !== 0) { - return true; - } + if (this[kDocuments].length !== 0) { + return true; + } - const doc = await nextAsync(this, true); + const doc = await nextAsync(this, true); - if (doc) { - this[kDocuments].unshift(doc); - return true; - } + if (doc) { + this[kDocuments].unshift(doc); + return true; + } - return false; - }, callback); + return false; } /** Get the next available document from the cursor, returns null if no more documents are available. */ - next(): Promise; - /** @deprecated Callbacks are deprecated and will be removed in the next major version. See [mongodb-legacy](https://github.com/mongodb-js/nodejs-mongodb-legacy) for migration assistance */ - next(callback: Callback): void; - /** @deprecated Callbacks are deprecated and will be removed in the next major version. See [mongodb-legacy](https://github.com/mongodb-js/nodejs-mongodb-legacy) for migration assistance */ - next(callback?: Callback): Promise | void; - next(callback?: Callback): Promise | void { - return maybeCallback(async () => { - if (this[kId] === Long.ZERO) { - throw new MongoCursorExhaustedError(); - } + async next(): Promise { + if (this[kId] === Long.ZERO) { + throw new MongoCursorExhaustedError(); + } - return nextAsync(this, true); - }, callback); + return nextAsync(this, true); } /** * Try to get the next available document from the cursor or `null` if an empty batch is returned */ - tryNext(): Promise; - /** @deprecated Callbacks are deprecated and will be removed in the next major version. See [mongodb-legacy](https://github.com/mongodb-js/nodejs-mongodb-legacy) for migration assistance */ - tryNext(callback: Callback): void; - tryNext(callback?: Callback): Promise | void { - return maybeCallback(async () => { - if (this[kId] === Long.ZERO) { - throw new MongoCursorExhaustedError(); - } + async tryNext(): Promise { + if (this[kId] === Long.ZERO) { + throw new MongoCursorExhaustedError(); + } - return nextAsync(this, false); - }, callback); + return nextAsync(this, false); } /** @@ -414,36 +397,23 @@ export abstract class AbstractCursor< * If the iterator returns `false`, iteration will stop. * * @param iterator - The iteration callback. - * @param callback - The end callback. */ - forEach(iterator: (doc: TSchema) => boolean | void): Promise; - /** @deprecated Callbacks are deprecated and will be removed in the next major version. See [mongodb-legacy](https://github.com/mongodb-js/nodejs-mongodb-legacy) for migration assistance */ - forEach(iterator: (doc: TSchema) => boolean | void, callback: Callback): void; - forEach( - iterator: (doc: TSchema) => boolean | void, - callback?: Callback - ): Promise | void { + async forEach(iterator: (doc: TSchema) => boolean | void): Promise { if (typeof iterator !== 'function') { throw new MongoInvalidArgumentError('Argument "iterator" must be a function'); } - return maybeCallback(async () => { - for await (const document of this) { - const result = iterator(document); - if (result === false) { - break; - } + for await (const document of this) { + const result = iterator(document); + if (result === false) { + break; } - }, callback); + } } - close(): Promise; - /** @deprecated Callbacks are deprecated and will be removed in the next major version. See [mongodb-legacy](https://github.com/mongodb-js/nodejs-mongodb-legacy) for migration assistance */ - close(callback: Callback): void; - close(callback?: Callback): Promise | void { + async close(): Promise { const needsToEmitClosed = !this[kClosed]; this[kClosed] = true; - - return maybeCallback(async () => cleanupCursorAsync(this, { needsToEmitClosed }), callback); + await cleanupCursorAsync(this, { needsToEmitClosed }); } /** @@ -451,20 +421,13 @@ export abstract class AbstractCursor< * is enough memory to store the results. Note that the array only contains partial * results when this cursor had been previously accessed. In that case, * cursor.rewind() can be used to reset the cursor. - * - * @param callback - The result callback. */ - toArray(): Promise; - /** @deprecated Callbacks are deprecated and will be removed in the next major version. See [mongodb-legacy](https://github.com/mongodb-js/nodejs-mongodb-legacy) for migration assistance */ - toArray(callback: Callback): void; - toArray(callback?: Callback): Promise | void { - return maybeCallback(async () => { - const array = []; - for await (const document of this) { - array.push(document); - } - return array; - }, callback); + async toArray(): Promise { + const array = []; + for await (const document of this) { + array.push(document); + } + return array; } /** @@ -903,7 +866,7 @@ class ReadableCursorStream extends Readable { } override _destroy(error: Error | null, callback: (error?: Error | null) => void): void { - this._cursor.close(err => process.nextTick(callback, err || error)); + this._cursor.close().finally(() => callback(error)); } private _readNext() { diff --git a/src/cursor/aggregation_cursor.ts b/src/cursor/aggregation_cursor.ts index 981989902fd..919f166519b 100644 --- a/src/cursor/aggregation_cursor.ts +++ b/src/cursor/aggregation_cursor.ts @@ -77,25 +77,14 @@ export class AggregationCursor extends AbstractCursor { } /** Execute the explain for the cursor */ - explain(): Promise; - explain(verbosity: ExplainVerbosityLike): Promise; - /** @deprecated Callbacks are deprecated and will be removed in the next major version. See [mongodb-legacy](https://github.com/mongodb-js/nodejs-mongodb-legacy) for migration assistance */ - explain(callback: Callback): void; - explain( - verbosity?: ExplainVerbosityLike | Callback, - callback?: Callback - ): Promise | void { - if (typeof verbosity === 'function') (callback = verbosity), (verbosity = true); - if (verbosity == null) verbosity = true; - + explain(verbosity?: ExplainVerbosityLike): Promise { return executeOperation( this.client, new AggregateOperation(this.namespace, this[kPipeline], { ...this[kOptions], // NOTE: order matters here, we may need to refine this ...this.cursorOptions, - explain: verbosity - }), - callback + explain: verbosity ?? true + }) ); } diff --git a/src/cursor/find_cursor.ts b/src/cursor/find_cursor.ts index 49f4f95cb28..c0d87358692 100644 --- a/src/cursor/find_cursor.ts +++ b/src/cursor/find_cursor.ts @@ -101,7 +101,8 @@ export class FindCursor extends AbstractCursor { limit && limit > 0 && numReturned + batchSize > limit ? limit - numReturned : batchSize; if (batchSize <= 0) { - return this.close(callback); + // @ts-expect-error: TODO... + return this.close().finally(() => callback()); } } @@ -121,58 +122,32 @@ export class FindCursor extends AbstractCursor { * Get the count of documents for this cursor * @deprecated Use `collection.estimatedDocumentCount` or `collection.countDocuments` instead */ - count(): Promise; - /** @deprecated Use `collection.estimatedDocumentCount` or `collection.countDocuments` instead. */ - count(options: CountOptions): Promise; - /** @deprecated Use `collection.estimatedDocumentCount` or `collection.countDocuments` instead. Callbacks are deprecated and will be removed in the next major version. See [mongodb-legacy](https://github.com/mongodb-js/nodejs-mongodb-legacy) for migration assistance */ - count(callback: Callback): void; - /** @deprecated Use `collection.estimatedDocumentCount` or `collection.countDocuments` instead. Callbacks are deprecated and will be removed in the next major version. See [mongodb-legacy](https://github.com/mongodb-js/nodejs-mongodb-legacy) for migration assistance */ - count(options: CountOptions, callback: Callback): void; - count( - options?: CountOptions | Callback, - callback?: Callback - ): Promise | void { + async count(options?: CountOptions): Promise { emitWarningOnce( 'cursor.count is deprecated and will be removed in the next major version, please use `collection.estimatedDocumentCount` or `collection.countDocuments` instead ' ); if (typeof options === 'boolean') { throw new MongoInvalidArgumentError('Invalid first parameter to count'); } - - if (typeof options === 'function') (callback = options), (options = {}); - options = options ?? {}; - return executeOperation( this.client, new CountOperation(this.namespace, this[kFilter], { ...this[kBuiltOptions], // NOTE: order matters here, we may need to refine this ...this.cursorOptions, ...options - }), - callback + }) ); } /** Execute the explain for the cursor */ - explain(): Promise; - explain(verbosity?: ExplainVerbosityLike): Promise; - /** @deprecated Callbacks are deprecated and will be removed in the next major version. See [mongodb-legacy](https://github.com/mongodb-js/nodejs-mongodb-legacy) for migration assistance */ - explain(callback: Callback): void; - explain( - verbosity?: ExplainVerbosityLike | Callback, - callback?: Callback - ): Promise | void { - if (typeof verbosity === 'function') (callback = verbosity), (verbosity = true); - if (verbosity == null) verbosity = true; - + async explain(verbosity?: ExplainVerbosityLike): Promise { return executeOperation( this.client, new FindOperation(undefined, this.namespace, this[kFilter], { ...this[kBuiltOptions], // NOTE: order matters here, we may need to refine this ...this.cursorOptions, - explain: verbosity - }), - callback + explain: verbosity ?? true + }) ); } From 5623462a9e8bbd89173d69468636983b23c4820a Mon Sep 17 00:00:00 2001 From: Neal Beeken Date: Tue, 20 Dec 2022 12:27:30 -0500 Subject: [PATCH 09/34] feat: remove callbacks from db.ts --- src/db.ts | 333 ++++++++---------------------------------------------- 1 file changed, 46 insertions(+), 287 deletions(-) diff --git a/src/db.ts b/src/db.ts index 29f97fb7723..49e9573163d 100644 --- a/src/db.ts +++ b/src/db.ts @@ -41,7 +41,6 @@ import { DbStatsOperation, DbStatsOptions } from './operations/stats'; import { ReadConcern } from './read_concern'; import { ReadPreference, ReadPreferenceLike } from './read_preference'; import { - Callback, DEFAULT_PK_FACTORY, filterOptions, MongoDBNamespace, @@ -226,35 +225,15 @@ export class Db { * * @param name - The name of the collection to create * @param options - Optional settings for the command - * @param callback - An optional callback, a Promise will be returned if none is provided */ - createCollection( + async createCollection( name: string, options?: CreateCollectionOptions - ): Promise>; - /** @deprecated Callbacks are deprecated and will be removed in the next major version. See [mongodb-legacy](https://github.com/mongodb-js/nodejs-mongodb-legacy) for migration assistance */ - createCollection( - name: string, - callback: Callback> - ): void; - /** @deprecated Callbacks are deprecated and will be removed in the next major version. See [mongodb-legacy](https://github.com/mongodb-js/nodejs-mongodb-legacy) for migration assistance */ - createCollection( - name: string, - options: CreateCollectionOptions | undefined, - callback: Callback> - ): void; - createCollection( - name: string, - options?: CreateCollectionOptions | Callback, - callback?: Callback - ): Promise> | void { - if (typeof options === 'function') (callback = options), (options = {}); - + ): Promise> { return executeOperation( this.s.client, - new CreateCollectionOperation(this, name, resolveOptions(this, options)) as TODO_NODE_3286, - callback - ) as TODO_NODE_3286; + new CreateCollectionOperation(this, name, resolveOptions(this, options)) as TODO_NODE_3286 + ); } /** @@ -265,27 +244,10 @@ export class Db { * * @param command - The command to run * @param options - Optional settings for the command - * @param callback - An optional callback, a Promise will be returned if none is provided */ - command(command: Document): Promise; - command(command: Document, options: RunCommandOptions): Promise; - /** @deprecated Callbacks are deprecated and will be removed in the next major version. See [mongodb-legacy](https://github.com/mongodb-js/nodejs-mongodb-legacy) for migration assistance */ - command(command: Document, callback: Callback): void; - /** @deprecated Callbacks are deprecated and will be removed in the next major version. See [mongodb-legacy](https://github.com/mongodb-js/nodejs-mongodb-legacy) for migration assistance */ - command(command: Document, options: RunCommandOptions, callback: Callback): void; - command( - command: Document, - options?: RunCommandOptions | Callback, - callback?: Callback - ): Promise | void { - if (typeof options === 'function') (callback = options), (options = {}); - + async command(command: Document, options?: RunCommandOptions): Promise { // Intentionally, we do not inherit options from parent for this operation. - return executeOperation( - this.s.client, - new RunCommandOperation(this, command, options ?? {}), - callback - ); + return executeOperation(this.s.client, new RunCommandOperation(this, command, options)); } /** @@ -298,16 +260,6 @@ export class Db { pipeline: Document[] = [], options?: AggregateOptions ): AggregationCursor { - if (arguments.length > 2) { - throw new MongoInvalidArgumentError('Method "db.aggregate()" accepts at most two arguments'); - } - if (typeof pipeline === 'function') { - throw new MongoInvalidArgumentError('Argument "pipeline" must not be function'); - } - if (typeof options === 'function') { - throw new MongoInvalidArgumentError('Argument "options" must not be function'); - } - return new AggregationCursor( this.s.client, this.s.namespace, @@ -334,31 +286,18 @@ export class Db { if (typeof options === 'function') { throw new MongoInvalidArgumentError('The callback form of this helper has been removed.'); } - const finalOptions = resolveOptions(this, options); - return new Collection(this, name, finalOptions); + return new Collection(this, name, resolveOptions(this, options)); } /** * Get all the db statistics. * * @param options - Optional settings for the command - * @param callback - An optional callback, a Promise will be returned if none is provided */ - stats(): Promise; - stats(options: DbStatsOptions): Promise; - /** @deprecated Callbacks are deprecated and will be removed in the next major version. See [mongodb-legacy](https://github.com/mongodb-js/nodejs-mongodb-legacy) for migration assistance */ - stats(callback: Callback): void; - /** @deprecated Callbacks are deprecated and will be removed in the next major version. See [mongodb-legacy](https://github.com/mongodb-js/nodejs-mongodb-legacy) for migration assistance */ - stats(options: DbStatsOptions, callback: Callback): void; - stats( - options?: DbStatsOptions | Callback, - callback?: Callback - ): Promise | void { - if (typeof options === 'function') (callback = options), (options = {}); + async stats(options?: DbStatsOptions): Promise { return executeOperation( this.s.client, - new DbStatsOperation(this, resolveOptions(this, options)), - callback + new DbStatsOperation(this, resolveOptions(this, options)) ); } @@ -398,52 +337,20 @@ export class Db { * @param fromCollection - Name of current collection to rename * @param toCollection - New name of of the collection * @param options - Optional settings for the command - * @param callback - An optional callback, a Promise will be returned if none is provided */ - renameCollection( - fromCollection: string, - toCollection: string - ): Promise>; - renameCollection( + async renameCollection( fromCollection: string, toCollection: string, - options: RenameOptions - ): Promise>; - /** @deprecated Callbacks are deprecated and will be removed in the next major version. See [mongodb-legacy](https://github.com/mongodb-js/nodejs-mongodb-legacy) for migration assistance */ - renameCollection( - fromCollection: string, - toCollection: string, - callback: Callback> - ): void; - /** @deprecated Callbacks are deprecated and will be removed in the next major version. See [mongodb-legacy](https://github.com/mongodb-js/nodejs-mongodb-legacy) for migration assistance */ - renameCollection( - fromCollection: string, - toCollection: string, - options: RenameOptions, - callback: Callback> - ): void; - renameCollection( - fromCollection: string, - toCollection: string, - options?: RenameOptions | Callback>, - callback?: Callback> - ): Promise> | void { - if (typeof options === 'function') (callback = options), (options = {}); - + options?: RenameOptions + ): Promise> { // Intentionally, we do not inherit options from parent for this operation. - options = { ...options, readPreference: ReadPreference.PRIMARY }; - - // Add return new collection - options.new_collection = true; - return executeOperation( this.s.client, new RenameOperation( this.collection(fromCollection) as TODO_NODE_3286, toCollection, - options - ) as TODO_NODE_3286, - callback + { ...options, new_collection: true, readPreference: ReadPreference.primary } + ) as TODO_NODE_3286 ); } @@ -452,25 +359,11 @@ export class Db { * * @param name - Name of collection to drop * @param options - Optional settings for the command - * @param callback - An optional callback, a Promise will be returned if none is provided */ - dropCollection(name: string): Promise; - dropCollection(name: string, options: DropCollectionOptions): Promise; - /** @deprecated Callbacks are deprecated and will be removed in the next major version. See [mongodb-legacy](https://github.com/mongodb-js/nodejs-mongodb-legacy) for migration assistance */ - dropCollection(name: string, callback: Callback): void; - /** @deprecated Callbacks are deprecated and will be removed in the next major version. See [mongodb-legacy](https://github.com/mongodb-js/nodejs-mongodb-legacy) for migration assistance */ - dropCollection(name: string, options: DropCollectionOptions, callback: Callback): void; - dropCollection( - name: string, - options?: DropCollectionOptions | Callback, - callback?: Callback - ): Promise | void { - if (typeof options === 'function') (callback = options), (options = {}); - + async dropCollection(name: string, options?: DropCollectionOptions): Promise { return executeOperation( this.s.client, - new DropCollectionOperation(this, name, resolveOptions(this, options)), - callback + new DropCollectionOperation(this, name, resolveOptions(this, options)) ); } @@ -478,24 +371,11 @@ export class Db { * Drop a database, removing it permanently from the server. * * @param options - Optional settings for the command - * @param callback - An optional callback, a Promise will be returned if none is provided */ - dropDatabase(): Promise; - dropDatabase(options: DropDatabaseOptions): Promise; - /** @deprecated Callbacks are deprecated and will be removed in the next major version. See [mongodb-legacy](https://github.com/mongodb-js/nodejs-mongodb-legacy) for migration assistance */ - dropDatabase(callback: Callback): void; - /** @deprecated Callbacks are deprecated and will be removed in the next major version. See [mongodb-legacy](https://github.com/mongodb-js/nodejs-mongodb-legacy) for migration assistance */ - dropDatabase(options: DropDatabaseOptions, callback: Callback): void; - dropDatabase( - options?: DropDatabaseOptions | Callback, - callback?: Callback - ): Promise | void { - if (typeof options === 'function') (callback = options), (options = {}); - + async dropDatabase(options?: DropDatabaseOptions): Promise { return executeOperation( this.s.client, - new DropDatabaseOperation(this, resolveOptions(this, options)), - callback + new DropDatabaseOperation(this, resolveOptions(this, options)) ); } @@ -503,24 +383,11 @@ export class Db { * Fetch all collections for the current db. * * @param options - Optional settings for the command - * @param callback - An optional callback, a Promise will be returned if none is provided */ - collections(): Promise; - collections(options: ListCollectionsOptions): Promise; - /** @deprecated Callbacks are deprecated and will be removed in the next major version. See [mongodb-legacy](https://github.com/mongodb-js/nodejs-mongodb-legacy) for migration assistance */ - collections(callback: Callback): void; - /** @deprecated Callbacks are deprecated and will be removed in the next major version. See [mongodb-legacy](https://github.com/mongodb-js/nodejs-mongodb-legacy) for migration assistance */ - collections(options: ListCollectionsOptions, callback: Callback): void; - collections( - options?: ListCollectionsOptions | Callback, - callback?: Callback - ): Promise | void { - if (typeof options === 'function') (callback = options), (options = {}); - + async collections(options?: ListCollectionsOptions): Promise { return executeOperation( this.s.client, - new CollectionsOperation(this, resolveOptions(this, options)), - callback + new CollectionsOperation(this, resolveOptions(this, options)) ); } @@ -530,35 +397,15 @@ export class Db { * @param name - Name of the collection to create the index on. * @param indexSpec - Specify the field to index, or an index specification * @param options - Optional settings for the command - * @param callback - An optional callback, a Promise will be returned if none is provided */ - createIndex(name: string, indexSpec: IndexSpecification): Promise; - createIndex( + async createIndex( name: string, indexSpec: IndexSpecification, - options: CreateIndexesOptions - ): Promise; - /** @deprecated Callbacks are deprecated and will be removed in the next major version. See [mongodb-legacy](https://github.com/mongodb-js/nodejs-mongodb-legacy) for migration assistance */ - createIndex(name: string, indexSpec: IndexSpecification, callback: Callback): void; - /** @deprecated Callbacks are deprecated and will be removed in the next major version. See [mongodb-legacy](https://github.com/mongodb-js/nodejs-mongodb-legacy) for migration assistance */ - createIndex( - name: string, - indexSpec: IndexSpecification, - options: CreateIndexesOptions, - callback: Callback - ): void; - createIndex( - name: string, - indexSpec: IndexSpecification, - options?: CreateIndexesOptions | Callback, - callback?: Callback - ): Promise | void { - if (typeof options === 'function') (callback = options), (options = {}); - + options?: CreateIndexesOptions + ): Promise { return executeOperation( this.s.client, - new CreateIndexOperation(this, name, indexSpec, resolveOptions(this, options)), - callback + new CreateIndexOperation(this, name, indexSpec, resolveOptions(this, options)) ); } @@ -568,47 +415,22 @@ export class Db { * @param username - The username for the new user * @param password - An optional password for the new user * @param options - Optional settings for the command - * @param callback - An optional callback, a Promise will be returned if none is provided */ - addUser(username: string): Promise; - addUser(username: string, password: string): Promise; - addUser(username: string, options: AddUserOptions): Promise; - addUser(username: string, password: string, options: AddUserOptions): Promise; - /** @deprecated Callbacks are deprecated and will be removed in the next major version. See [mongodb-legacy](https://github.com/mongodb-js/nodejs-mongodb-legacy) for migration assistance */ - addUser(username: string, callback: Callback): void; - /** @deprecated Callbacks are deprecated and will be removed in the next major version. See [mongodb-legacy](https://github.com/mongodb-js/nodejs-mongodb-legacy) for migration assistance */ - addUser(username: string, password: string, callback: Callback): void; - /** @deprecated Callbacks are deprecated and will be removed in the next major version. See [mongodb-legacy](https://github.com/mongodb-js/nodejs-mongodb-legacy) for migration assistance */ - addUser(username: string, options: AddUserOptions, callback: Callback): void; - /** @deprecated Callbacks are deprecated and will be removed in the next major version. See [mongodb-legacy](https://github.com/mongodb-js/nodejs-mongodb-legacy) for migration assistance */ - addUser( - username: string, - password: string, - options: AddUserOptions, - callback: Callback - ): void; - addUser( + async addUser( username: string, - password?: string | AddUserOptions | Callback, - options?: AddUserOptions | Callback, - callback?: Callback - ): Promise | void { - if (typeof password === 'function') { - (callback = password), (password = undefined), (options = {}); - } else if (typeof password !== 'string') { - if (typeof options === 'function') { - (callback = options), (options = password), (password = undefined); - } else { - (options = password), (callback = undefined), (password = undefined); - } - } else { - if (typeof options === 'function') (callback = options), (options = {}); - } - + password?: string | AddUserOptions, + options?: AddUserOptions + ): Promise { + options = + options != null && typeof options === 'object' + ? options + : password != null && typeof password === 'object' + ? password + : undefined; + password = typeof password === 'string' ? password : undefined; return executeOperation( this.s.client, - new AddUserOperation(this, username, password, resolveOptions(this, options)), - callback + new AddUserOperation(this, username, password, resolveOptions(this, options)) ); } @@ -617,25 +439,11 @@ export class Db { * * @param username - The username to remove * @param options - Optional settings for the command - * @param callback - An optional callback, a Promise will be returned if none is provided */ - removeUser(username: string): Promise; - removeUser(username: string, options: RemoveUserOptions): Promise; - /** @deprecated Callbacks are deprecated and will be removed in the next major version. See [mongodb-legacy](https://github.com/mongodb-js/nodejs-mongodb-legacy) for migration assistance */ - removeUser(username: string, callback: Callback): void; - /** @deprecated Callbacks are deprecated and will be removed in the next major version. See [mongodb-legacy](https://github.com/mongodb-js/nodejs-mongodb-legacy) for migration assistance */ - removeUser(username: string, options: RemoveUserOptions, callback: Callback): void; - removeUser( - username: string, - options?: RemoveUserOptions | Callback, - callback?: Callback - ): Promise | void { - if (typeof options === 'function') (callback = options), (options = {}); - + async removeUser(username: string, options?: RemoveUserOptions): Promise { return executeOperation( this.s.client, - new RemoveUserOperation(this, username, resolveOptions(this, options)), - callback + new RemoveUserOperation(this, username, resolveOptions(this, options)) ); } @@ -644,32 +452,14 @@ export class Db { * * @param level - The new profiling level (off, slow_only, all). * @param options - Optional settings for the command - * @param callback - An optional callback, a Promise will be returned if none is provided */ - setProfilingLevel(level: ProfilingLevel): Promise; - setProfilingLevel( - level: ProfilingLevel, - options: SetProfilingLevelOptions - ): Promise; - /** @deprecated Callbacks are deprecated and will be removed in the next major version. See [mongodb-legacy](https://github.com/mongodb-js/nodejs-mongodb-legacy) for migration assistance */ - setProfilingLevel(level: ProfilingLevel, callback: Callback): void; - /** @deprecated Callbacks are deprecated and will be removed in the next major version. See [mongodb-legacy](https://github.com/mongodb-js/nodejs-mongodb-legacy) for migration assistance */ - setProfilingLevel( - level: ProfilingLevel, - options: SetProfilingLevelOptions, - callback: Callback - ): void; - setProfilingLevel( + async setProfilingLevel( level: ProfilingLevel, - options?: SetProfilingLevelOptions | Callback, - callback?: Callback - ): Promise | void { - if (typeof options === 'function') (callback = options), (options = {}); - + options?: SetProfilingLevelOptions + ): Promise { return executeOperation( this.s.client, - new SetProfilingLevelOperation(this, level, resolveOptions(this, options)), - callback + new SetProfilingLevelOperation(this, level, resolveOptions(this, options)) ); } @@ -677,24 +467,11 @@ export class Db { * Retrieve the current profiling Level for MongoDB * * @param options - Optional settings for the command - * @param callback - An optional callback, a Promise will be returned if none is provided */ - profilingLevel(): Promise; - profilingLevel(options: ProfilingLevelOptions): Promise; - /** @deprecated Callbacks are deprecated and will be removed in the next major version. See [mongodb-legacy](https://github.com/mongodb-js/nodejs-mongodb-legacy) for migration assistance */ - profilingLevel(callback: Callback): void; - /** @deprecated Callbacks are deprecated and will be removed in the next major version. See [mongodb-legacy](https://github.com/mongodb-js/nodejs-mongodb-legacy) for migration assistance */ - profilingLevel(options: ProfilingLevelOptions, callback: Callback): void; - profilingLevel( - options?: ProfilingLevelOptions | Callback, - callback?: Callback - ): Promise | void { - if (typeof options === 'function') (callback = options), (options = {}); - + async profilingLevel(options?: ProfilingLevelOptions): Promise { return executeOperation( this.s.client, - new ProfilingLevelOperation(this, resolveOptions(this, options)), - callback + new ProfilingLevelOperation(this, resolveOptions(this, options)) ); } @@ -703,29 +480,11 @@ export class Db { * * @param name - The name of the collection. * @param options - Optional settings for the command - * @param callback - An optional callback, a Promise will be returned if none is provided */ - indexInformation(name: string): Promise; - indexInformation(name: string, options: IndexInformationOptions): Promise; - /** @deprecated Callbacks are deprecated and will be removed in the next major version. See [mongodb-legacy](https://github.com/mongodb-js/nodejs-mongodb-legacy) for migration assistance */ - indexInformation(name: string, callback: Callback): void; - /** @deprecated Callbacks are deprecated and will be removed in the next major version. See [mongodb-legacy](https://github.com/mongodb-js/nodejs-mongodb-legacy) for migration assistance */ - indexInformation( - name: string, - options: IndexInformationOptions, - callback: Callback - ): void; - indexInformation( - name: string, - options?: IndexInformationOptions | Callback, - callback?: Callback - ): Promise | void { - if (typeof options === 'function') (callback = options), (options = {}); - + async indexInformation(name: string, options?: IndexInformationOptions): Promise { return executeOperation( this.s.client, - new IndexInformationOperation(this, name, resolveOptions(this, options)), - callback + new IndexInformationOperation(this, name, resolveOptions(this, options)) ); } From 53ad68e7703d764605a5bb7650e9259a5f9287f7 Mon Sep 17 00:00:00 2001 From: Neal Beeken Date: Tue, 20 Dec 2022 12:52:10 -0500 Subject: [PATCH 10/34] feat: remove callbacks from mongo_client.ts --- src/mongo_client.ts | 206 +++++++++++++++++--------------------------- 1 file changed, 78 insertions(+), 128 deletions(-) diff --git a/src/mongo_client.ts b/src/mongo_client.ts index 63b46cf9ecb..cc5fe1de3a6 100644 --- a/src/mongo_client.ts +++ b/src/mongo_client.ts @@ -26,7 +26,6 @@ import type { SrvPoller } from './sdam/srv_polling'; import { Topology, TopologyEvents } from './sdam/topology'; import { ClientSession, ClientSessionOptions, ServerSessionPool } from './sessions'; import { - Callback, ClientMetadata, HostAddress, maybeCallback, @@ -425,79 +424,60 @@ export class MongoClient extends TypedEventEmitter { * * @see docs.mongodb.org/manual/reference/connection-string/ */ - connect(): Promise; - /** @deprecated Callbacks are deprecated and will be removed in the next major version. See [mongodb-legacy](https://github.com/mongodb-js/nodejs-mongodb-legacy) for migration assistance */ - connect(callback: Callback): void; - connect(callback?: Callback): Promise | void { - if (callback && typeof callback !== 'function') { - throw new MongoInvalidArgumentError('Method `connect` only accepts a callback'); + async connect(): Promise { + if (this.topology && this.topology.isConnected()) { + return this; } - return maybeCallback(async () => { - if (this.topology && this.topology.isConnected()) { - return this; - } - - const options = this[kOptions]; + const options = this[kOptions]; - if (typeof options.srvHost === 'string') { - const hosts = await resolveSRVRecord(options); + if (typeof options.srvHost === 'string') { + const hosts = await resolveSRVRecord(options); - for (const [index, host] of hosts.entries()) { - options.hosts[index] = host; - } + for (const [index, host] of hosts.entries()) { + options.hosts[index] = host; } + } - const topology = new Topology(options.hosts, options); - // Events can be emitted before initialization is complete so we have to - // save the reference to the topology on the client ASAP if the event handlers need to access it - this.topology = topology; - topology.client = this; + const topology = new Topology(options.hosts, options); + // Events can be emitted before initialization is complete so we have to + // save the reference to the topology on the client ASAP if the event handlers need to access it + this.topology = topology; + topology.client = this; - topology.once(Topology.OPEN, () => this.emit('open', this)); + topology.once(Topology.OPEN, () => this.emit('open', this)); - for (const event of MONGO_CLIENT_EVENTS) { - topology.on(event, (...args: any[]) => this.emit(event, ...(args as any))); - } + for (const event of MONGO_CLIENT_EVENTS) { + topology.on(event, (...args: any[]) => this.emit(event, ...(args as any))); + } - const topologyConnect = async () => { - try { - await promisify(callback => topology.connect(options, callback))(); - } catch (error) { - topology.close({ force: true }); - throw error; - } - }; - - if (this.autoEncrypter) { - const initAutoEncrypter = promisify(callback => this.autoEncrypter?.init(callback)); - await initAutoEncrypter(); - await topologyConnect(); - await options.encrypter.connectInternalClient(); - } else { - await topologyConnect(); + const topologyConnect = async () => { + try { + await promisify(callback => topology.connect(options, callback))(); + } catch (error) { + topology.close({ force: true }); + throw error; } + }; - return this; - }, callback); + if (this.autoEncrypter) { + const initAutoEncrypter = promisify(callback => this.autoEncrypter?.init(callback)); + await initAutoEncrypter(); + await topologyConnect(); + await options.encrypter.connectInternalClient(); + } else { + await topologyConnect(); + } + + return this; } /** - * Close the db and its underlying connections + * Close the client and its underlying connections * * @param force - Force close, emitting no events - * @param callback - An optional callback, a Promise will be returned if none is provided */ - close(): Promise; - close(force: boolean): Promise; - /** @deprecated Callbacks are deprecated and will be removed in the next major version. See [mongodb-legacy](https://github.com/mongodb-js/nodejs-mongodb-legacy) for migration assistance */ - close(callback: Callback): void; - /** @deprecated Callbacks are deprecated and will be removed in the next major version. See [mongodb-legacy](https://github.com/mongodb-js/nodejs-mongodb-legacy) for migration assistance */ - close(force: boolean, callback: Callback): void; - close( - forceOrCallback?: boolean | Callback, - callback?: Callback - ): Promise | void { + async close(force = false): Promise { // There's no way to set hasBeenClosed back to false Object.defineProperty(this.s, 'hasBeenClosed', { value: true, @@ -506,58 +486,50 @@ export class MongoClient extends TypedEventEmitter { writable: false }); - if (typeof forceOrCallback === 'function') { - callback = forceOrCallback; - } - - const force = typeof forceOrCallback === 'boolean' ? forceOrCallback : false; + const activeSessionEnds = Array.from(this.s.activeSessions, session => session.endSession()); + this.s.activeSessions.clear(); - return maybeCallback(async () => { - const activeSessionEnds = Array.from(this.s.activeSessions, session => session.endSession()); - this.s.activeSessions.clear(); + await Promise.all(activeSessionEnds); - await Promise.all(activeSessionEnds); + if (this.topology == null) { + return; + } - if (this.topology == null) { - return; + // If we would attempt to select a server and get nothing back we short circuit + // to avoid the server selection timeout. + const selector = readPreferenceServerSelector(ReadPreference.primaryPreferred); + const topologyDescription = this.topology.description; + const serverDescriptions = Array.from(topologyDescription.servers.values()); + const servers = selector(topologyDescription, serverDescriptions); + if (servers.length !== 0) { + const endSessions = Array.from(this.s.sessionPool.sessions, ({ id }) => id); + if (endSessions.length !== 0) { + await this.db('admin') + .command( + { endSessions }, + { readPreference: ReadPreference.primaryPreferred, noResponse: true } + ) + .catch(() => null); // outcome does not matter } + } - // If we would attempt to select a server and get nothing back we short circuit - // to avoid the server selection timeout. - const selector = readPreferenceServerSelector(ReadPreference.primaryPreferred); - const topologyDescription = this.topology.description; - const serverDescriptions = Array.from(topologyDescription.servers.values()); - const servers = selector(topologyDescription, serverDescriptions); - if (servers.length !== 0) { - const endSessions = Array.from(this.s.sessionPool.sessions, ({ id }) => id); - if (endSessions.length !== 0) { - await this.db('admin') - .command( - { endSessions }, - { readPreference: ReadPreference.primaryPreferred, noResponse: true } - ) - .catch(() => null); // outcome does not matter + // clear out references to old topology + const topology = this.topology; + this.topology = undefined; + + await new Promise((resolve, reject) => { + topology.close({ force }, error => { + if (error) return reject(error); + const { encrypter } = this[kOptions]; + if (encrypter) { + return encrypter.close(this, force, error => { + if (error) return reject(error); + resolve(); + }); } - } - - // clear out references to old topology - const topology = this.topology; - this.topology = undefined; - - await new Promise((resolve, reject) => { - topology.close({ force }, error => { - if (error) return reject(error); - const { encrypter } = this[kOptions]; - if (encrypter) { - return encrypter.close(this, force, error => { - if (error) return reject(error); - resolve(); - }); - } - resolve(); - }); + resolve(); }); - }, callback); + }); } /** @@ -592,34 +564,12 @@ export class MongoClient extends TypedEventEmitter { * * @see https://docs.mongodb.org/manual/reference/connection-string/ */ - static connect(url: string): Promise; - static connect(url: string, options: MongoClientOptions): Promise; - /** @deprecated Callbacks are deprecated and will be removed in the next major version. See [mongodb-legacy](https://github.com/mongodb-js/nodejs-mongodb-legacy) for migration assistance */ - static connect(url: string, callback: Callback): void; - /** @deprecated Callbacks are deprecated and will be removed in the next major version. See [mongodb-legacy](https://github.com/mongodb-js/nodejs-mongodb-legacy) for migration assistance */ - static connect(url: string, options: MongoClientOptions, callback: Callback): void; - static connect( - url: string, - options?: MongoClientOptions | Callback, - callback?: Callback - ): Promise | void { - callback = - typeof callback === 'function' - ? callback - : typeof options === 'function' - ? options - : undefined; - - return maybeCallback(async () => { - options = typeof options !== 'function' ? options : undefined; - const client = new this(url, options); - return client.connect(); - }, callback); + static async connect(url: string, options?: MongoClientOptions): Promise { + const client = new this(url, options); + return client.connect(); } /** Starts a new session on the server */ - startSession(): ClientSession; - startSession(options: ClientSessionOptions): ClientSession; startSession(options?: ClientSessionOptions): ClientSession { const session = new ClientSession( this, @@ -646,7 +596,7 @@ export class MongoClient extends TypedEventEmitter { withSession(callback: WithSessionCallback): Promise; withSession(options: ClientSessionOptions, callback: WithSessionCallback): Promise; withSession( - optionsOrOperation?: ClientSessionOptions | WithSessionCallback, + optionsOrOperation: ClientSessionOptions | WithSessionCallback, callback?: WithSessionCallback ): Promise { const options = { From 51b0649b17d4be98c158ed620e9ce5cb6e7b31f3 Mon Sep 17 00:00:00 2001 From: Neal Beeken Date: Tue, 20 Dec 2022 12:55:53 -0500 Subject: [PATCH 11/34] feat: remove callbacks from sessions.ts --- src/sessions.ts | 80 +++++++++++++++++-------------------------------- 1 file changed, 27 insertions(+), 53 deletions(-) diff --git a/src/sessions.ts b/src/sessions.ts index 1774f7c7806..99fc9d11603 100644 --- a/src/sessions.ts +++ b/src/sessions.ts @@ -36,7 +36,6 @@ import { isPromiseLike, List, maxWireVersion, - maybeCallback, now, uuidV4 } from './utils'; @@ -246,47 +245,32 @@ export class ClientSession extends TypedEventEmitter { * Ends this session on the server * * @param options - Optional settings. Currently reserved for future use - * @param callback - Optional callback for completion of this operation */ - endSession(): Promise; - endSession(options: EndSessionOptions): Promise; - /** @deprecated Callbacks are deprecated and will be removed in the next major version. See [mongodb-legacy](https://github.com/mongodb-js/nodejs-mongodb-legacy) for migration assistance */ - endSession(callback: Callback): void; - /** @deprecated Callbacks are deprecated and will be removed in the next major version. See [mongodb-legacy](https://github.com/mongodb-js/nodejs-mongodb-legacy) for migration assistance */ - endSession(options: EndSessionOptions, callback: Callback): void; - endSession( - options?: EndSessionOptions | Callback, - callback?: Callback - ): void | Promise { - if (typeof options === 'function') (callback = options), (options = {}); - const finalOptions = { force: true, ...options }; - - return maybeCallback(async () => { - try { - if (this.inTransaction()) { - await this.abortTransaction(); - } - if (!this.hasEnded) { - const serverSession = this[kServerSession]; - if (serverSession != null) { - // release the server session back to the pool - this.sessionPool.release(serverSession); - // Make sure a new serverSession never makes it onto this ClientSession - Object.defineProperty(this, kServerSession, { - value: ServerSession.clone(serverSession), - writable: false - }); - } - // mark the session as ended, and emit a signal - this.hasEnded = true; - this.emit('ended', this); + async endSession(options?: EndSessionOptions): Promise { + try { + if (this.inTransaction()) { + await this.abortTransaction(); + } + if (!this.hasEnded) { + const serverSession = this[kServerSession]; + if (serverSession != null) { + // release the server session back to the pool + this.sessionPool.release(serverSession); + // Make sure a new serverSession never makes it onto this ClientSession + Object.defineProperty(this, kServerSession, { + value: ServerSession.clone(serverSession), + writable: false + }); } - } catch { - // spec indicates that we should ignore all errors for `endSessions` - } finally { - maybeClearPinnedConnection(this, finalOptions); + // mark the session as ended, and emit a signal + this.hasEnded = true; + this.emit('ended', this); } - }, callback); + } catch { + // spec indicates that we should ignore all errors for `endSessions` + } finally { + maybeClearPinnedConnection(this, { force: true, ...options }); + } } /** @@ -420,26 +404,16 @@ export class ClientSession extends TypedEventEmitter { /** * Commits the currently active transaction in this session. - * - * @param callback - An optional callback, a Promise will be returned if none is provided */ - commitTransaction(): Promise; - /** @deprecated Callbacks are deprecated and will be removed in the next major version. See [mongodb-legacy](https://github.com/mongodb-js/nodejs-mongodb-legacy) for migration assistance */ - commitTransaction(callback: Callback): void; - commitTransaction(callback?: Callback): Promise | void { - return maybeCallback(async () => endTransactionAsync(this, 'commitTransaction'), callback); + async commitTransaction(): Promise { + return endTransactionAsync(this, 'commitTransaction'); } /** * Aborts the currently active transaction in this session. - * - * @param callback - An optional callback, a Promise will be returned if none is provided */ - abortTransaction(): Promise; - /** @deprecated Callbacks are deprecated and will be removed in the next major version. See [mongodb-legacy](https://github.com/mongodb-js/nodejs-mongodb-legacy) for migration assistance */ - abortTransaction(callback: Callback): void; - abortTransaction(callback?: Callback): Promise | void { - return maybeCallback(async () => endTransactionAsync(this, 'abortTransaction'), callback); + async abortTransaction(): Promise { + return endTransactionAsync(this, 'abortTransaction'); } /** From bfaf3c9dec77a5e4cf411b24ded3d532344e934e Mon Sep 17 00:00:00 2001 From: Neal Beeken Date: Tue, 20 Dec 2022 15:06:04 -0500 Subject: [PATCH 12/34] feat: remove callbacks from gridfs --- src/gridfs/download.ts | 20 +++++---------- src/gridfs/index.ts | 58 +++++++++++++++--------------------------- src/gridfs/upload.ts | 31 +++++++++------------- 3 files changed, 39 insertions(+), 70 deletions(-) diff --git a/src/gridfs/download.ts b/src/gridfs/download.ts index 9e96151478b..0053ecc9a09 100644 --- a/src/gridfs/download.ts +++ b/src/gridfs/download.ts @@ -182,30 +182,22 @@ export class GridFSBucketReadStream extends Readable implements NodeJS.ReadableS * Marks this stream as aborted (will never push another `data` event) * and kills the underlying cursor. Will emit the 'end' event, and then * the 'close' event once the cursor is successfully killed. - * - * @param callback - called when the cursor is successfully closed or an error occurred. */ - abort(callback?: Callback): void { + async abort(): Promise { this.push(null); this.destroyed = true; if (this.s.cursor) { - this.s.cursor.close().then( - () => { - this.emit(GridFSBucketReadStream.CLOSE); - callback?.(); - }, - error => { - this.emit(GridFSBucketReadStream.CLOSE); - callback?.(error); - } - ); + try { + await this.s.cursor.close(); + } finally { + this.emit(GridFSBucketReadStream.CLOSE); + } } else { if (!this.s.init) { // If not initialized, fire close event because we will never // get a cursor this.emit(GridFSBucketReadStream.CLOSE); } - callback && callback(); } } } diff --git a/src/gridfs/index.ts b/src/gridfs/index.ts index 874762b0132..d090626786f 100644 --- a/src/gridfs/index.ts +++ b/src/gridfs/index.ts @@ -7,7 +7,6 @@ import type { Logger } from '../logger'; import { Filter, TypedEventEmitter } from '../mongo_types'; import type { ReadPreference } from '../read_preference'; import type { Sort } from '../sort'; -import { Callback, maybeCallback } from '../utils'; import { WriteConcern, WriteConcernOptions } from '../write_concern'; import type { FindOptions } from './../operations/find'; import { @@ -140,22 +139,17 @@ export class GridFSBucket extends TypedEventEmitter { * * @param id - The id of the file doc */ - delete(id: ObjectId): Promise; - /** @deprecated Callbacks are deprecated and will be removed in the next major version. See [mongodb-legacy](https://github.com/mongodb-js/nodejs-mongodb-legacy) for migration assistance */ - delete(id: ObjectId, callback: Callback): void; - delete(id: ObjectId, callback?: Callback): Promise | void { - return maybeCallback(async () => { - const { deletedCount } = await this.s._filesCollection.deleteOne({ _id: id }); - - // Delete orphaned chunks before returning FileNotFound - await this.s._chunksCollection.deleteMany({ files_id: id }); - - if (deletedCount === 0) { - // TODO(NODE-3483): Replace with more appropriate error - // Consider creating new error MongoGridFSFileNotFoundError - throw new MongoRuntimeError(`File not found for id ${id}`); - } - }, callback); + async delete(id: ObjectId): Promise { + const { deletedCount } = await this.s._filesCollection.deleteOne({ _id: id }); + + // Delete orphaned chunks before returning FileNotFound + await this.s._chunksCollection.deleteMany({ files_id: id }); + + if (deletedCount === 0) { + // TODO(NODE-3483): Replace with more appropriate error + // Consider creating new error MongoGridFSFileNotFoundError + throw new MongoRuntimeError(`File not found for id ${id}`); + } } /** Convenience wrapper around find on the files collection */ @@ -201,29 +195,19 @@ export class GridFSBucket extends TypedEventEmitter { * @param id - the id of the file to rename * @param filename - new name for the file */ - rename(id: ObjectId, filename: string): Promise; - /** @deprecated Callbacks are deprecated and will be removed in the next major version. See [mongodb-legacy](https://github.com/mongodb-js/nodejs-mongodb-legacy) for migration assistance */ - rename(id: ObjectId, filename: string, callback: Callback): void; - rename(id: ObjectId, filename: string, callback?: Callback): Promise | void { - return maybeCallback(async () => { - const filter = { _id: id }; - const update = { $set: { filename } }; - const { matchedCount } = await this.s._filesCollection.updateOne(filter, update); - if (matchedCount === 0) { - throw new MongoRuntimeError(`File with id ${id} not found`); - } - }, callback); + async rename(id: ObjectId, filename: string): Promise { + const filter = { _id: id }; + const update = { $set: { filename } }; + const { matchedCount } = await this.s._filesCollection.updateOne(filter, update); + if (matchedCount === 0) { + throw new MongoRuntimeError(`File with id ${id} not found`); + } } /** Removes this bucket's files collection, followed by its chunks collection. */ - drop(): Promise; - /** @deprecated Callbacks are deprecated and will be removed in the next major version. See [mongodb-legacy](https://github.com/mongodb-js/nodejs-mongodb-legacy) for migration assistance */ - drop(callback: Callback): void; - drop(callback?: Callback): Promise | void { - return maybeCallback(async () => { - await this.s._filesCollection.drop(); - await this.s._chunksCollection.drop(); - }, callback); + async drop(): Promise { + await this.s._filesCollection.drop(); + await this.s._chunksCollection.drop(); } /** Get the Db scoped logger. */ diff --git a/src/gridfs/upload.ts b/src/gridfs/upload.ts index e36c026d51a..a5dabc564dd 100644 --- a/src/gridfs/upload.ts +++ b/src/gridfs/upload.ts @@ -4,7 +4,7 @@ import type { Document } from '../bson'; import { ObjectId } from '../bson'; import type { Collection } from '../collection'; import { AnyError, MongoAPIError, MONGODB_ERROR_CODES, MongoError } from '../error'; -import { Callback, maybeCallback } from '../utils'; +import type { Callback } from '../utils'; import type { WriteConcernOptions } from '../write_concern'; import { WriteConcern } from './../write_concern'; import type { GridFSFile } from './download'; @@ -144,27 +144,20 @@ export class GridFSBucketWriteStream extends Writable implements NodeJS.Writable /** * Places this write stream into an aborted state (all future writes fail) * and deletes all chunks that have already been written. - * - * @param callback - called when chunks are successfully removed or error occurred */ - abort(): Promise; - /** @deprecated Callbacks are deprecated and will be removed in the next major version. See [mongodb-legacy](https://github.com/mongodb-js/nodejs-mongodb-legacy) for migration assistance */ - abort(callback: Callback): void; - abort(callback?: Callback): Promise | void { - return maybeCallback(async () => { - if (this.state.streamEnd) { - // TODO(NODE-3485): Replace with MongoGridFSStreamClosed - throw new MongoAPIError('Cannot abort a stream that has already completed'); - } + async abort(): Promise { + if (this.state.streamEnd) { + // TODO(NODE-3485): Replace with MongoGridFSStreamClosed + throw new MongoAPIError('Cannot abort a stream that has already completed'); + } - if (this.state.aborted) { - // TODO(NODE-3485): Replace with MongoGridFSStreamClosed - throw new MongoAPIError('Cannot call abort() on a stream twice'); - } + if (this.state.aborted) { + // TODO(NODE-3485): Replace with MongoGridFSStreamClosed + throw new MongoAPIError('Cannot call abort() on a stream twice'); + } - this.state.aborted = true; - await this.chunks.deleteMany({ files_id: this.id }); - }, callback); + this.state.aborted = true; + await this.chunks.deleteMany({ files_id: this.id }); } /** From 217101dcb663d670818f26946c337b9b6f6cc655 Mon Sep 17 00:00:00 2001 From: Neal Beeken Date: Thu, 19 Jan 2023 18:43:53 -0500 Subject: [PATCH 13/34] async keyword withTransaction and agg.explain --- src/cursor/aggregation_cursor.ts | 2 +- src/sessions.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/cursor/aggregation_cursor.ts b/src/cursor/aggregation_cursor.ts index 919f166519b..f57969d39b8 100644 --- a/src/cursor/aggregation_cursor.ts +++ b/src/cursor/aggregation_cursor.ts @@ -77,7 +77,7 @@ export class AggregationCursor extends AbstractCursor { } /** Execute the explain for the cursor */ - explain(verbosity?: ExplainVerbosityLike): Promise { + async explain(verbosity?: ExplainVerbosityLike): Promise { return executeOperation( this.client, new AggregateOperation(this.namespace, this[kPipeline], { diff --git a/src/sessions.ts b/src/sessions.ts index 99fc9d11603..81b45dd59e6 100644 --- a/src/sessions.ts +++ b/src/sessions.ts @@ -444,7 +444,7 @@ export class ClientSession extends TypedEventEmitter { * @param options - optional settings for the transaction * @returns A raw command response or undefined */ - withTransaction( + async withTransaction( fn: WithTransactionCallback, options?: TransactionOptions ): Promise { From ffcd2d8e822d3a28681e0d8ce984c39f7d4cc016 Mon Sep 17 00:00:00 2001 From: Neal Beeken Date: Fri, 20 Jan 2023 12:58:43 -0500 Subject: [PATCH 14/34] fle: bump to promise first fle commit --- .evergreen/config.yml | 6 +++--- .evergreen/generate_evergreen_tasks.js | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/.evergreen/config.yml b/.evergreen/config.yml index 4baf064da7e..148b327f310 100644 --- a/.evergreen/config.yml +++ b/.evergreen/config.yml @@ -2132,7 +2132,7 @@ tasks: - func: bootstrap kms servers - func: run custom csfle tests vars: - CSFLE_GIT_REF: ff9e095eaf72f9e442761f69080dae159a395d94 + CSFLE_GIT_REF: 3b3d0ebd2b180f4cb63adf937ecd985fce7e217b - name: run-custom-csfle-tests-5.0-master tags: - run-custom-dependency-tests @@ -2162,7 +2162,7 @@ tasks: - func: bootstrap kms servers - func: run custom csfle tests vars: - CSFLE_GIT_REF: ff9e095eaf72f9e442761f69080dae159a395d94 + CSFLE_GIT_REF: 3b3d0ebd2b180f4cb63adf937ecd985fce7e217b - name: run-custom-csfle-tests-rapid-master tags: - run-custom-dependency-tests @@ -2192,7 +2192,7 @@ tasks: - func: bootstrap kms servers - func: run custom csfle tests vars: - CSFLE_GIT_REF: ff9e095eaf72f9e442761f69080dae159a395d94 + CSFLE_GIT_REF: 3b3d0ebd2b180f4cb63adf937ecd985fce7e217b - name: run-custom-csfle-tests-latest-master tags: - run-custom-dependency-tests diff --git a/.evergreen/generate_evergreen_tasks.js b/.evergreen/generate_evergreen_tasks.js index 4d98400ff85..2fc5938a375 100644 --- a/.evergreen/generate_evergreen_tasks.js +++ b/.evergreen/generate_evergreen_tasks.js @@ -579,7 +579,7 @@ BUILD_VARIANTS.push({ const oneOffFuncAsTasks = [] for (const version of ['5.0', 'rapid', 'latest']) { - for (const ref of ['ff9e095eaf72f9e442761f69080dae159a395d94', 'master']) { + for (const ref of ['3b3d0ebd2b180f4cb63adf937ecd985fce7e217b', 'master']) { oneOffFuncAsTasks.push({ name: `run-custom-csfle-tests-${version}-${ref === 'master' ? ref : 'pinned-commit'}`, tags: ['run-custom-dependency-tests'], From 5f43565594e38d64e81f5c82d77de82d1fd87a1f Mon Sep 17 00:00:00 2001 From: Neal Beeken Date: Fri, 20 Jan 2023 13:14:25 -0500 Subject: [PATCH 15/34] fix: withSession --- src/mongo_client.ts | 31 +++++++++++-------------------- 1 file changed, 11 insertions(+), 20 deletions(-) diff --git a/src/mongo_client.ts b/src/mongo_client.ts index cc5fe1de3a6..575f42aaa1d 100644 --- a/src/mongo_client.ts +++ b/src/mongo_client.ts @@ -25,14 +25,7 @@ import { readPreferenceServerSelector } from './sdam/server_selection'; import type { SrvPoller } from './sdam/srv_polling'; import { Topology, TopologyEvents } from './sdam/topology'; import { ClientSession, ClientSessionOptions, ServerSessionPool } from './sessions'; -import { - ClientMetadata, - HostAddress, - maybeCallback, - MongoDBNamespace, - ns, - resolveOptions -} from './utils'; +import { ClientMetadata, HostAddress, MongoDBNamespace, ns, resolveOptions } from './utils'; import type { W, WriteConcern, WriteConcernSettings } from './write_concern'; /** @public */ @@ -593,9 +586,9 @@ export class MongoClient extends TypedEventEmitter { * @param options - Optional settings for the command * @param callback - An callback to execute with an implicitly created session */ - withSession(callback: WithSessionCallback): Promise; - withSession(options: ClientSessionOptions, callback: WithSessionCallback): Promise; - withSession( + async withSession(callback: WithSessionCallback): Promise; + async withSession(options: ClientSessionOptions, callback: WithSessionCallback): Promise; + async withSession( optionsOrOperation: ClientSessionOptions | WithSessionCallback, callback?: WithSessionCallback ): Promise { @@ -615,17 +608,15 @@ export class MongoClient extends TypedEventEmitter { const session = this.startSession(options); - return maybeCallback(async () => { + try { + await withSessionCallback(session); + } finally { try { - await withSessionCallback(session); - } finally { - try { - await session.endSession(); - } catch { - // We are not concerned with errors from endSession() - } + await session.endSession(); + } catch { + // We are not concerned with errors from endSession() } - }, null); + } } /** From c3f2769703f343c43f0b2a31c4107eab77531007 Mon Sep 17 00:00:00 2001 From: Neal Beeken Date: Fri, 20 Jan 2023 13:16:16 -0500 Subject: [PATCH 16/34] rm test file --- test/unit/mongodb-legacy.test.ts | 9 --------- 1 file changed, 9 deletions(-) delete mode 100644 test/unit/mongodb-legacy.test.ts diff --git a/test/unit/mongodb-legacy.test.ts b/test/unit/mongodb-legacy.test.ts deleted file mode 100644 index 2bcceeb0e0e..00000000000 --- a/test/unit/mongodb-legacy.test.ts +++ /dev/null @@ -1,9 +0,0 @@ -import { expect } from 'chai'; - -import { Db } from '../mongodb'; - -describe('mongodb-legacy', () => { - it('imports a Db with the legacy symbol', () => { - expect(Db.prototype).to.have.property(Symbol.for('@@mdb.callbacks.toLegacy')); - }); -}); From 4d67771c83a8cc43efab9577defb6f3af67393fe Mon Sep 17 00:00:00 2001 From: Neal Beeken Date: Fri, 20 Jan 2023 13:55:15 -0500 Subject: [PATCH 17/34] fix: lint --- src/db.ts | 7 +--- .../bulk/bulk-operation-base.test-d.ts | 31 --------------- test/types/community/client.test-d.ts | 25 ------------ .../community/collection/distinct.test-d.ts | 22 ----------- .../community/collection/findX.test-d.ts | 34 ++-------------- .../community/db/createCollection.test-d.ts | 39 +------------------ test/types/indexes_test-d.ts | 13 +------ 7 files changed, 6 insertions(+), 165 deletions(-) diff --git a/src/db.ts b/src/db.ts index 49e9573163d..7195dc37cc6 100644 --- a/src/db.ts +++ b/src/db.ts @@ -40,12 +40,7 @@ import { import { DbStatsOperation, DbStatsOptions } from './operations/stats'; import { ReadConcern } from './read_concern'; import { ReadPreference, ReadPreferenceLike } from './read_preference'; -import { - DEFAULT_PK_FACTORY, - filterOptions, - MongoDBNamespace, - resolveOptions -} from './utils'; +import { DEFAULT_PK_FACTORY, filterOptions, MongoDBNamespace, resolveOptions } from './utils'; import { WriteConcern, WriteConcernOptions } from './write_concern'; // Allowed parameters diff --git a/test/types/community/bulk/bulk-operation-base.test-d.ts b/test/types/community/bulk/bulk-operation-base.test-d.ts index 29564ff393e..cc2e5ecb59e 100644 --- a/test/types/community/bulk/bulk-operation-base.test-d.ts +++ b/test/types/community/bulk/bulk-operation-base.test-d.ts @@ -1,13 +1,11 @@ import { expectType } from 'tsd'; import { - AnyError, Batch, BatchType, BulkOperationBase, BulkWriteOptions, BulkWriteResult, - Callback, DeleteStatement, Document, MongoClient, @@ -51,32 +49,3 @@ function extendedPromiseBasedBulkExecute( } expectType>(extendedPromiseBasedBulkExecute()); - -expectType( - bulkOperation.execute((error, bulkWriteResult) => { - expectType(error); - expectType(bulkWriteResult); - }) -); - -expectType( - bulkOperation.execute(options, (error, bulkWriteResult) => { - expectType(error); - expectType(bulkWriteResult); - }) -); - -// ensure we can use the bulk operation execute in a callback based wrapper function -function extendedCallbackBasedBulkExecute( - callback: Callback, - optionalOptions?: BulkWriteOptions -): void { - bulkOperation.execute(optionalOptions, callback); -} - -expectType( - extendedCallbackBasedBulkExecute((error, bulkWriteResult) => { - expectType(error); - expectType(bulkWriteResult); - }) -); diff --git a/test/types/community/client.test-d.ts b/test/types/community/client.test-d.ts index 5a067616f97..1362631e456 100644 --- a/test/types/community/client.test-d.ts +++ b/test/types/community/client.test-d.ts @@ -4,7 +4,6 @@ import { GridFSBucket, MongoClient, MongoClientOptions, - MongoError, MongoNetworkError, MongoParseError, ReadPreference, @@ -38,25 +37,11 @@ const options: MongoClientOptions = { directConnection: false }; -MongoClient.connect(connectionString, options, (err, client?: MongoClient) => { - if (err || !client) throw err; - const db = client.db('test'); - db.collection('test_crud'); - // Let's close the db - client.close(); -}); - export async function testFunc(): Promise { const testClient: MongoClient = await MongoClient.connect(connectionString); return testClient; } -MongoClient.connect(connectionString, err => { - if (err instanceof MongoError) { - expectType(err.hasErrorLabel('label')); - } -}); - expectType>(MongoClient.connect(connectionString, options)); // TLS @@ -83,16 +68,6 @@ export function gridTest(bucket: GridFSBucket): void { openUploadStream.on('close', () => {}); openUploadStream.on('end', () => {}); expectType>(openUploadStream.abort()); // $ExpectType void - expectType( - openUploadStream.abort(() => { - openUploadStream.removeAllListeners(); - }) - ); - openUploadStream.abort(error => { - error; // $ExpectType MongoError - }); - // eslint-disable-next-line @typescript-eslint/no-unused-vars - openUploadStream.abort((error, result) => {}); } // Client-Side Field Level Encryption diff --git a/test/types/community/collection/distinct.test-d.ts b/test/types/community/collection/distinct.test-d.ts index 5bc87e01319..8d0ffac48b3 100644 --- a/test/types/community/collection/distinct.test-d.ts +++ b/test/types/community/collection/distinct.test-d.ts @@ -18,32 +18,10 @@ expectType(await collection.distinct('test')); expectType(await collection.distinct('test', { foo: 1 })); expectType(await collection.distinct('test', { foo: 1 }, { maxTimeMS: 400 })); -collection.distinct('_id', (err, fields) => { - // callbacks always have the second argument undefined - // incase of error - expectType(fields); -}); -collection.distinct('_id', { foo: 1 }, (err, fields) => { - expectType(fields); -}); -collection.distinct('_id', { foo: 1 }, { maxTimeMS: 400 }, (err, fields) => { - expectType(fields); -}); - expectType(await collection.distinct('_id')); expectType(await collection.distinct('_id', { foo: 1 })); expectType(await collection.distinct('_id', { foo: 1 }, { maxTimeMS: 400 })); -collection.distinct('nested.num', (err, fields) => { - expectType(fields); -}); -collection.distinct('nested.num', { foo: 1 }, (err, fields) => { - expectType(fields); -}); -collection.distinct('nested.num', { foo: 1 }, { maxTimeMS: 400 }, (err, fields) => { - expectType(fields); -}); - expectType(await collection.distinct('nested.num')); expectType(await collection.distinct('nested.num', { foo: 1 })); expectType(await collection.distinct('nested.num', { foo: 1 }, { maxTimeMS: 400 })); diff --git a/test/types/community/collection/findX.test-d.ts b/test/types/community/collection/findX.test-d.ts index e0ccd4eb92d..216239be9fd 100644 --- a/test/types/community/collection/findX.test-d.ts +++ b/test/types/community/collection/findX.test-d.ts @@ -4,7 +4,6 @@ import type { Filter } from '../../../../src'; import { Collection, Db, - Document, FindCursor, FindOptions, MongoClient, @@ -19,13 +18,9 @@ const db = client.db('test'); const collection = db.collection('test.find'); // Locate all the entries using find -collection.find({}).toArray((_err, fields) => { - expectType[] | undefined>(fields); - if (fields) { - expectType(fields[0]._id); - expectNotType(fields[0]._id); - } -}); +const fields = await collection.find({}).toArray(); +expectType(fields[0]._id); +expectNotType(fields[0]._id); // test with collection type interface TestModel { @@ -86,21 +81,6 @@ interface Bag { const collectionBag = db.collection('bag'); -const cursor: FindCursor> = collectionBag.find({ color: 'black' }); - -cursor.toArray((_err, bags) => { - expectType[] | undefined>(bags); -}); - -cursor.forEach( - bag => { - expectType>(bag); - }, - () => { - return null; - } -); - expectType | null>( await collectionBag.findOne({ color: 'red' }, { projection: { cost: 1 } }) ); @@ -253,14 +233,6 @@ const typedDb = client.db('test2') as TypedDb; const person = typedDb.collection('people').findOne({}); expectType | null>>(person); -typedDb.collection('people').findOne({}, function (_err, person) { - expectType | null | undefined>(person); // null is if nothing is found, undefined is when there is an error defined -}); - -typedDb.collection('things').findOne({}, function (_err, thing) { - expectType | null | undefined>(thing); -}); - interface SchemaWithTypicalId { _id: ObjectId; name: string; diff --git a/test/types/community/db/createCollection.test-d.ts b/test/types/community/db/createCollection.test-d.ts index 9e932fd5cb0..0830804bb9d 100644 --- a/test/types/community/db/createCollection.test-d.ts +++ b/test/types/community/db/createCollection.test-d.ts @@ -1,13 +1,6 @@ import { expectType } from 'tsd'; -import { - AnyError, - Callback, - Collection, - CreateCollectionOptions, - MongoClient, - ObjectId -} from '../../../mongodb'; +import { Collection, CreateCollectionOptions, MongoClient, ObjectId } from '../../../mongodb'; const client = new MongoClient(''); const db = client.db('test'); @@ -50,33 +43,3 @@ function extendedPromiseBasedCreateCollection( } expectType>>(extendedPromiseBasedCreateCollection('test')); - -expectType( - db.createCollection('test', (err, collection) => { - expectType(err); - expectType | undefined>(collection); - }) -); - -expectType( - db.createCollection('test', options, (err, collection) => { - expectType(err); - expectType | undefined>(collection); - }) -); - -// ensure we can use the create collection in a callback based wrapper function -function extendedCallbackBasedCreateCollection( - name: string, - callback: Callback>, - optionalOptions?: CreateCollectionOptions -): void { - db.createCollection(name, optionalOptions, callback); -} - -expectType( - extendedCallbackBasedCreateCollection('test', (err, collection) => { - expectType(err); - expectType | undefined>(collection); - }) -); diff --git a/test/types/indexes_test-d.ts b/test/types/indexes_test-d.ts index 430559a5fc8..34ae9d4a182 100644 --- a/test/types/indexes_test-d.ts +++ b/test/types/indexes_test-d.ts @@ -1,6 +1,6 @@ import { expectType } from 'tsd'; -import { AnyError, Document, MongoClient } from '../../src'; +import { Document, MongoClient } from '../../src'; const client = new MongoClient(''); const db = client.db('test'); @@ -14,14 +14,3 @@ expectType>(collection.indexes({})); for (const index of await collection.indexes()) { expectType(index); } - -// Callback variant testing -collection.indexes((err, indexes) => { - expectType(err); - expectType(indexes); -}); - -collection.indexes({}, (err, indexes) => { - expectType(err); - expectType(indexes); -}); From 7a015e5c0bcaeb938926816c05e8cf7c731658b3 Mon Sep 17 00:00:00 2001 From: Neal Beeken Date: Fri, 20 Jan 2023 14:16:38 -0500 Subject: [PATCH 18/34] fix and skip tests --- .../change-streams/change_stream.test.ts | 18 +++++------ .../collection-management/collection.test.ts | 15 +++++----- .../crud/find_cursor_methods.test.js | 3 +- test/integration/crud/insert.test.js | 30 +++++++------------ test/integration/crud/misc_cursors.test.js | 6 ++-- 5 files changed, 31 insertions(+), 41 deletions(-) diff --git a/test/integration/change-streams/change_stream.test.ts b/test/integration/change-streams/change_stream.test.ts index 05074716af2..213a5bd628d 100644 --- a/test/integration/change-streams/change_stream.test.ts +++ b/test/integration/change-streams/change_stream.test.ts @@ -749,16 +749,14 @@ describe('Change Streams', function () { const errRegex = /ChangeStream cannot be used as an iterator/; - // These all throw synchronously so it should be safe to not await the results - expect(() => { - changeStream.next(); - }).to.throw(errRegex); - expect(() => { - changeStream.hasNext(); - }).to.throw(errRegex); - expect(() => { - changeStream.tryNext(); - }).to.throw(errRegex); + const nextError = await changeStream.next().catch(error => error); + expect(nextError.message).to.match(errRegex); + + const hasNextError = await changeStream.hasNext().catch(error => error); + expect(hasNextError.message).to.match(errRegex); + + const tryNextError = await changeStream.tryNext().catch(error => error); + expect(tryNextError.message).to.match(errRegex); } }); diff --git a/test/integration/collection-management/collection.test.ts b/test/integration/collection-management/collection.test.ts index d7fba712315..f58d58138ab 100644 --- a/test/integration/collection-management/collection.test.ts +++ b/test/integration/collection-management/collection.test.ts @@ -241,15 +241,14 @@ describe('Collection', function () { }); }); - it('should throw error due to illegal update', function (done) { - db.createCollection('shouldThrowErrorDueToIllegalUpdate', {}, (err, coll) => { - expect(() => coll.update({}, null)).to.throw(/Document must be a valid JavaScript object/); - expect(() => coll.update(null, null)).to.throw( - /Selector must be a valid JavaScript object/ - ); + it('should throw error due to illegal update', async function () { + const coll = db.createCollection('shouldThrowErrorDueToIllegalUpdate', {}); - done(); - }); + const filterError = await coll.updateOne({}, null).catch(error => error); + expect(filterError.message).to.match(/Document must be a valid JavaScript object/); + + const updateError = await coll.updateOne({}, null).catch(error => error); + expect(updateError.message).to.match(/Selector must be a valid JavaScript object/); }); const selectorTests = [ diff --git a/test/integration/crud/find_cursor_methods.test.js b/test/integration/crud/find_cursor_methods.test.js index 278ddeca143..b41cff4857e 100644 --- a/test/integration/crud/find_cursor_methods.test.js +++ b/test/integration/crud/find_cursor_methods.test.js @@ -204,7 +204,8 @@ describe('Find Cursor', function () { }); }); - context('#clone', function () { + // TODO(NODE-XXXX): Need to fix cursor.clone in mongodb-legacy + context.skip('#clone', function () { it('should clone a find cursor', function (done) { const coll = client.db().collection('abstract_cursor'); const cursor = coll.find({}); diff --git a/test/integration/crud/insert.test.js b/test/integration/crud/insert.test.js index 268a282eb3e..3a9b8a3a2c9 100644 --- a/test/integration/crud/insert.test.js +++ b/test/integration/crud/insert.test.js @@ -18,7 +18,8 @@ const { MaxKey, Code, MongoBulkWriteError, - ReturnDocument + ReturnDocument, + MongoInvalidArgumentError } = require('../../mongodb'); /** @@ -92,25 +93,14 @@ describe('crud - insert', function () { }); }); - it('Should correctly return failing Promise when no document array passed into insertMany', function (done) { - const configuration = this.configuration; - let url = configuration.url(); - url = - url.indexOf('?') !== -1 - ? f('%s&%s', url, 'maxPoolSize=100') - : f('%s?%s', url, 'maxPoolSize=100'); - - const client = configuration.newClient(url); - client.connect().then(() => { - this.defer(() => client.close()); - - const db = client.db(configuration.db); - expect(() => { - db.collection('insertMany_Promise_error').insertMany({ a: 1 }); - }).to.throw(/Argument "docs" must be an array of documents/); - - done(); - }); + it('rejects when insertMany is passed a non array object', async function () { + const db = client.db(); + const error = db + .collection('insertMany_Promise_error') + .insertMany({ a: 1 }) + .catch(error => error); + expect(error).to.be.instanceOf(MongoInvalidArgumentError); + expect(error.message).to.match(/must be an array/); }); describe('collection.insert()', function () { diff --git a/test/integration/crud/misc_cursors.test.js b/test/integration/crud/misc_cursors.test.js index 463be72d118..e99848606d2 100644 --- a/test/integration/crud/misc_cursors.test.js +++ b/test/integration/crud/misc_cursors.test.js @@ -1671,7 +1671,8 @@ describe('Cursor', function () { } }); - it('removes session wheen cloning a find cursor', function (done) { + // TODO(NODE-XXXX): Need to fix cursor.clone in mongodb-legacy + it.skip('removes session wheen cloning a find cursor', function (done) { const configuration = this.configuration; client.connect((err, client) => { expect(err).to.not.exist; @@ -1698,7 +1699,8 @@ describe('Cursor', function () { }); }); - it('removes session wheen cloning an aggregation cursor', { + // TODO(NODE-XXXX): Need to fix cursor.clone in mongodb-legacy + it.skip('removes session wheen cloning an aggregation cursor', { metadata: { requires: { topology: ['single', 'replicaset', 'sharded'] } }, From 84f4f9a3dec4ceb4bd1cf2ea00d822dc75d7dec3 Mon Sep 17 00:00:00 2001 From: Neal Beeken Date: Fri, 20 Jan 2023 14:20:37 -0500 Subject: [PATCH 19/34] jira ticket --- test/integration/crud/find_cursor_methods.test.js | 2 +- test/integration/crud/misc_cursors.test.js | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/test/integration/crud/find_cursor_methods.test.js b/test/integration/crud/find_cursor_methods.test.js index b41cff4857e..fd5bf23d1cd 100644 --- a/test/integration/crud/find_cursor_methods.test.js +++ b/test/integration/crud/find_cursor_methods.test.js @@ -204,7 +204,7 @@ describe('Find Cursor', function () { }); }); - // TODO(NODE-XXXX): Need to fix cursor.clone in mongodb-legacy + // TODO(NODE-4988): Need to fix cursor.clone in mongodb-legacy context.skip('#clone', function () { it('should clone a find cursor', function (done) { const coll = client.db().collection('abstract_cursor'); diff --git a/test/integration/crud/misc_cursors.test.js b/test/integration/crud/misc_cursors.test.js index e99848606d2..c27ae1313ee 100644 --- a/test/integration/crud/misc_cursors.test.js +++ b/test/integration/crud/misc_cursors.test.js @@ -1671,7 +1671,7 @@ describe('Cursor', function () { } }); - // TODO(NODE-XXXX): Need to fix cursor.clone in mongodb-legacy + // TODO(NODE-4988): Need to fix cursor.clone in mongodb-legacy it.skip('removes session wheen cloning a find cursor', function (done) { const configuration = this.configuration; client.connect((err, client) => { @@ -1699,7 +1699,7 @@ describe('Cursor', function () { }); }); - // TODO(NODE-XXXX): Need to fix cursor.clone in mongodb-legacy + // TODO(NODE-4988): Need to fix cursor.clone in mongodb-legacy it.skip('removes session wheen cloning an aggregation cursor', { metadata: { requires: { topology: ['single', 'replicaset', 'sharded'] } From 7c061966f5c6db2ebff16360e8a9190c93873583 Mon Sep 17 00:00:00 2001 From: Neal Beeken Date: Fri, 20 Jan 2023 14:51:31 -0500 Subject: [PATCH 20/34] fix: await things --- test/integration/collection-management/collection.test.ts | 2 +- test/integration/crud/insert.test.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/test/integration/collection-management/collection.test.ts b/test/integration/collection-management/collection.test.ts index f58d58138ab..697d06a1349 100644 --- a/test/integration/collection-management/collection.test.ts +++ b/test/integration/collection-management/collection.test.ts @@ -242,7 +242,7 @@ describe('Collection', function () { }); it('should throw error due to illegal update', async function () { - const coll = db.createCollection('shouldThrowErrorDueToIllegalUpdate', {}); + const coll = await db.createCollection('shouldThrowErrorDueToIllegalUpdate', {}); const filterError = await coll.updateOne({}, null).catch(error => error); expect(filterError.message).to.match(/Document must be a valid JavaScript object/); diff --git a/test/integration/crud/insert.test.js b/test/integration/crud/insert.test.js index 3a9b8a3a2c9..d443014d849 100644 --- a/test/integration/crud/insert.test.js +++ b/test/integration/crud/insert.test.js @@ -95,7 +95,7 @@ describe('crud - insert', function () { it('rejects when insertMany is passed a non array object', async function () { const db = client.db(); - const error = db + const error = await db .collection('insertMany_Promise_error') .insertMany({ a: 1 }) .catch(error => error); From c1d5738153563fbd54111f4c91546d46e8df6525 Mon Sep 17 00:00:00 2001 From: Neal Beeken Date: Fri, 20 Jan 2023 15:10:14 -0500 Subject: [PATCH 21/34] flip messages to check --- test/integration/collection-management/collection.test.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/test/integration/collection-management/collection.test.ts b/test/integration/collection-management/collection.test.ts index 697d06a1349..5b4cac76acc 100644 --- a/test/integration/collection-management/collection.test.ts +++ b/test/integration/collection-management/collection.test.ts @@ -244,11 +244,11 @@ describe('Collection', function () { it('should throw error due to illegal update', async function () { const coll = await db.createCollection('shouldThrowErrorDueToIllegalUpdate', {}); - const filterError = await coll.updateOne({}, null).catch(error => error); - expect(filterError.message).to.match(/Document must be a valid JavaScript object/); + const filterError = await coll.updateOne(null, {}).catch(error => error); + expect(filterError.message).to.match(/Selector must be a valid JavaScript object/); const updateError = await coll.updateOne({}, null).catch(error => error); - expect(updateError.message).to.match(/Selector must be a valid JavaScript object/); + expect(updateError.message).to.match(/Document must be a valid JavaScript object/); }); const selectorTests = [ From 266ec54b2ec0aa3e40d0c0fcde8b3966ee24b3a0 Mon Sep 17 00:00:00 2001 From: Neal Beeken Date: Fri, 20 Jan 2023 17:37:33 -0500 Subject: [PATCH 22/34] use fle alpha --- .evergreen/run-tests.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.evergreen/run-tests.sh b/.evergreen/run-tests.sh index 0b7ddc95135..4cb73282455 100755 --- a/.evergreen/run-tests.sh +++ b/.evergreen/run-tests.sh @@ -52,7 +52,7 @@ else source "$DRIVERS_TOOLS"/.evergreen/csfle/set-temp-creds.sh fi -npm install mongodb-client-encryption@">=2.3.0" +npm install mongodb-client-encryption@">=2.4.0-alpha.0" npm install @mongodb-js/zstd npm install snappy From 2a1ee77ea50a0a925335bb286bf8d3dc5a73f965 Mon Sep 17 00:00:00 2001 From: Neal Beeken Date: Fri, 20 Jan 2023 18:23:09 -0500 Subject: [PATCH 23/34] round one: fight --- src/admin.ts | 12 ++++++------ src/bulk/common.ts | 4 +--- src/collection.ts | 6 ------ src/cursor/find_cursor.ts | 4 ++-- src/operations/validate_collection.ts | 3 +-- 5 files changed, 10 insertions(+), 19 deletions(-) diff --git a/src/admin.ts b/src/admin.ts index 711cc90e681..9e9a64e0821 100644 --- a/src/admin.ts +++ b/src/admin.ts @@ -104,21 +104,21 @@ export class Admin { * Add a user to the database * * @param username - The username for the new user - * @param password - An optional password for the new user + * @param passwordOrOptions - An optional password for the new user, or the options for the command * @param options - Optional settings for the command */ async addUser( username: string, - password?: string | AddUserOptions, + passwordOrOptions?: string | AddUserOptions, options?: AddUserOptions ): Promise { options = options != null && typeof options === 'object' ? options - : password != null && typeof password === 'object' - ? password + : passwordOrOptions != null && typeof passwordOrOptions === 'object' + ? passwordOrOptions : undefined; - password = typeof password === 'string' ? password : undefined; + const password = typeof passwordOrOptions === 'string' ? passwordOrOptions : undefined; return executeOperation( this.s.db.s.client, new AddUserOperation(this.s.db, username, password, { dbName: 'admin', ...options }) @@ -146,7 +146,7 @@ export class Admin { */ async validateCollection( collectionName: string, - options?: ValidateCollectionOptions + options: ValidateCollectionOptions = {} ): Promise { return executeOperation( this.s.db.s.client, diff --git a/src/bulk/common.ts b/src/bulk/common.ts index 51ad8256781..566c2464b98 100644 --- a/src/bulk/common.ts +++ b/src/bulk/common.ts @@ -1174,9 +1174,7 @@ export abstract class BulkOperationBase { return batches; } - async execute(options?: BulkWriteOptions): Promise { - options ??= {}; - + async execute(options: BulkWriteOptions = {}): Promise { if (this.s.executed) { throw new MongoBatchReExecutionError(); } diff --git a/src/collection.ts b/src/collection.ts index a530e744cd2..38e46a39baf 100644 --- a/src/collection.ts +++ b/src/collection.ts @@ -258,12 +258,6 @@ export class Collection { doc: OptionalUnlessRequiredId, options?: InsertOneOptions ): Promise> { - // TODO(NODE-4751): versions of mongodb-client-encryption before v1.2.6 pass in hardcoded { w: 'majority' } - // specifically to an insertOne call in createDataKey, so we want to support this only here - if (options && Reflect.get(options, 'w')) { - options.writeConcern = WriteConcern.fromOptions(Reflect.get(options, 'w')); - } - return executeOperation( this.s.db.s.client, new InsertOneOperation( diff --git a/src/cursor/find_cursor.ts b/src/cursor/find_cursor.ts index c0d87358692..f79f52b9c36 100644 --- a/src/cursor/find_cursor.ts +++ b/src/cursor/find_cursor.ts @@ -101,8 +101,8 @@ export class FindCursor extends AbstractCursor { limit && limit > 0 && numReturned + batchSize > limit ? limit - numReturned : batchSize; if (batchSize <= 0) { - // @ts-expect-error: TODO... - return this.close().finally(() => callback()); + this.close().finally(() => callback()); + return; } } diff --git a/src/operations/validate_collection.ts b/src/operations/validate_collection.ts index fee124f4781..ba6c85fad05 100644 --- a/src/operations/validate_collection.ts +++ b/src/operations/validate_collection.ts @@ -18,9 +18,8 @@ export class ValidateCollectionOperation extends CommandOperation { collectionName: string; command: Document; - constructor(admin: Admin, collectionName: string, options?: ValidateCollectionOptions) { + constructor(admin: Admin, collectionName: string, options: ValidateCollectionOptions) { // Decorate command with extra options - options ??= {}; const command: Document = { validate: collectionName }; const keys = Object.keys(options); for (let i = 0; i < keys.length; i++) { From c12c40147c1a0da819e008a2ebf1835a00a3d809 Mon Sep 17 00:00:00 2001 From: Neal Beeken Date: Fri, 20 Jan 2023 22:52:01 -0500 Subject: [PATCH 24/34] comments --- src/db.ts | 10 +-- .../crud/find_cursor_methods.test.js | 53 +++++--------- test/integration/crud/misc_cursors.test.js | 70 +++++-------------- 3 files changed, 40 insertions(+), 93 deletions(-) diff --git a/src/db.ts b/src/db.ts index 7195dc37cc6..298edfacf45 100644 --- a/src/db.ts +++ b/src/db.ts @@ -408,21 +408,21 @@ export class Db { * Add a user to the database * * @param username - The username for the new user - * @param password - An optional password for the new user + * @param passwordOrOptions - An optional password for the new user, or the options for the command * @param options - Optional settings for the command */ async addUser( username: string, - password?: string | AddUserOptions, + passwordOrOptions?: string | AddUserOptions, options?: AddUserOptions ): Promise { options = options != null && typeof options === 'object' ? options - : password != null && typeof password === 'object' - ? password + : passwordOrOptions != null && typeof passwordOrOptions === 'object' + ? passwordOrOptions : undefined; - password = typeof password === 'string' ? password : undefined; + const password = typeof passwordOrOptions === 'string' ? passwordOrOptions : undefined; return executeOperation( this.s.client, new AddUserOperation(this, username, password, resolveOptions(this, options)) diff --git a/test/integration/crud/find_cursor_methods.test.js b/test/integration/crud/find_cursor_methods.test.js index fd5bf23d1cd..69324efaa04 100644 --- a/test/integration/crud/find_cursor_methods.test.js +++ b/test/integration/crud/find_cursor_methods.test.js @@ -204,50 +204,35 @@ describe('Find Cursor', function () { }); }); - // TODO(NODE-4988): Need to fix cursor.clone in mongodb-legacy - context.skip('#clone', function () { - it('should clone a find cursor', function (done) { + context('#clone', function () { + it('should clone a find cursor', async function () { + // TODO(NODE-4988): Need to fix cursor.clone in mongodb-legacy, callbacks do not work on cloned cursor const coll = client.db().collection('abstract_cursor'); const cursor = coll.find({}); - this.defer(() => cursor.close()); - - cursor.toArray((err, docs) => { - expect(err).to.not.exist; - expect(docs).to.have.length(6); - expect(cursor).property('closed').to.be.true; - const clonedCursor = cursor.clone(); - this.defer(() => clonedCursor.close()); + const docsFromOriginal = await cursor.toArray(); + expect(docsFromOriginal).to.have.length(6); + expect(cursor).property('closed').to.be.true; - clonedCursor.toArray((err, docs) => { - expect(err).to.not.exist; - expect(docs).to.have.length(6); - expect(clonedCursor).property('closed').to.be.true; - done(); - }); - }); + const clonedCursor = cursor.clone(); + const docsFromCloned = await clonedCursor.toArray(); + expect(docsFromCloned).to.have.length(6); + expect(cursor).property('closed').to.be.true; }); - it('should clone an aggregate cursor', function (done) { + it('should clone an aggregate cursor', async function () { + // TODO(NODE-4988): Need to fix cursor.clone in mongodb-legacy, callbacks do not work on cloned cursor const coll = client.db().collection('abstract_cursor'); const cursor = coll.aggregate([{ $match: {} }]); - this.defer(() => cursor.close()); - - cursor.toArray((err, docs) => { - expect(err).to.not.exist; - expect(docs).to.have.length(6); - expect(cursor).property('closed').to.be.true; - const clonedCursor = cursor.clone(); - this.defer(() => clonedCursor.close()); + const docsFromOriginal = await cursor.toArray(); + expect(docsFromOriginal).to.have.length(6); + expect(cursor).property('closed').to.be.true; - clonedCursor.toArray((err, docs) => { - expect(err).to.not.exist; - expect(docs).to.have.length(6); - expect(clonedCursor).property('closed').to.be.true; - done(); - }); - }); + const clonedCursor = cursor.clone(); + const docsFromCloned = await clonedCursor.toArray(); + expect(docsFromCloned).to.have.length(6); + expect(cursor).property('closed').to.be.true; }); }); diff --git a/test/integration/crud/misc_cursors.test.js b/test/integration/crud/misc_cursors.test.js index c27ae1313ee..ba5b2bfe909 100644 --- a/test/integration/crud/misc_cursors.test.js +++ b/test/integration/crud/misc_cursors.test.js @@ -1671,66 +1671,28 @@ describe('Cursor', function () { } }); - // TODO(NODE-4988): Need to fix cursor.clone in mongodb-legacy - it.skip('removes session wheen cloning a find cursor', function (done) { - const configuration = this.configuration; - client.connect((err, client) => { - expect(err).to.not.exist; - - const db = client.db(configuration.db); - db.createCollection('clone_find_cursor_session', (err, collection) => { - expect(err).to.not.exist; + it('removes session when cloning an aggregation cursor', async function () { + // TODO(NODE-4988): Need to fix cursor.clone in mongodb-legacy, callbacks do not work on cloned cursor + const collection = await client.db().collection(); - collection.insertOne({ a: 1 }, configuration.writeConcernMax(), err => { - expect(err).to.not.exist; + const cursor = collection.find({}); + const clonedCursor = cursor.clone(); - const cursor = collection.find(); - const clonedCursor = cursor.clone(); - cursor.toArray(err => { - expect(err).to.not.exist; - clonedCursor.toArray(err => { - expect(err).to.not.exist; - client.close(); - done(); - }); - }); - }); - }); - }); + expect(cursor).to.have.property('session'); + expect(clonedCursor).to.have.property('session'); + expect(cursor.session).to.not.equal(clonedCursor.session); }); - // TODO(NODE-4988): Need to fix cursor.clone in mongodb-legacy - it.skip('removes session wheen cloning an aggregation cursor', { - metadata: { - requires: { topology: ['single', 'replicaset', 'sharded'] } - }, - - test: function (done) { - const configuration = this.configuration; - client.connect((err, client) => { - expect(err).to.not.exist; - - const db = client.db(configuration.db); - db.createCollection('clone_aggregation_cursor_session', (err, collection) => { - expect(err).to.not.exist; + it('removes session when cloning an aggregation cursor', async function () { + // TODO(NODE-4988): Need to fix cursor.clone in mongodb-legacy, callbacks do not work on cloned cursor + const collection = await client.db().collection(); - collection.insertOne({ a: 1 }, configuration.writeConcernMax(), err => { - expect(err).to.not.exist; + const cursor = collection.aggregate([{ $match: {} }]); + const clonedCursor = cursor.clone(); - const cursor = collection.aggregate([{ $match: { a: 1 } }]); - const clonedCursor = cursor.clone(); - cursor.toArray(err => { - expect(err).to.not.exist; - clonedCursor.toArray(err => { - expect(err).to.not.exist; - client.close(); - done(); - }); - }); - }); - }); - }); - } + expect(cursor).to.have.property('session'); + expect(clonedCursor).to.have.property('session'); + expect(cursor.session).to.not.equal(clonedCursor.session); }); it('destroying a stream stops it', { From fb12143d7dd64503fa42e88eef882531387515e2 Mon Sep 17 00:00:00 2001 From: Neal Beeken Date: Fri, 20 Jan 2023 23:28:56 -0500 Subject: [PATCH 25/34] consistent default arguments --- src/collection.ts | 16 ++++++++-------- src/cursor/find_cursor.ts | 4 ++-- src/gridfs/index.ts | 4 +--- 3 files changed, 11 insertions(+), 13 deletions(-) diff --git a/src/collection.ts b/src/collection.ts index 38e46a39baf..616ba80fdf3 100644 --- a/src/collection.ts +++ b/src/collection.ts @@ -725,8 +725,10 @@ export class Collection { * @see https://docs.mongodb.com/manual/reference/operator/query/center/#op._S_center * @see https://docs.mongodb.com/manual/reference/operator/query/centerSphere/#op._S_centerSphere */ - async countDocuments(filter?: Document, options?: CountDocumentsOptions): Promise { - filter ??= {}; + async countDocuments( + filter: Document = {}, + options: CountDocumentsOptions = {} + ): Promise { return executeOperation( this.s.db.s.client, new CountDocumentsOperation(this as TODO_NODE_3286, filter, resolveOptions(this, options)) @@ -760,17 +762,16 @@ export class Collection { async distinct>( key: Key, - filter?: Filter, - options?: DistinctOptions + filter: Filter = {}, + options: DistinctOptions = {} ): Promise { - filter ??= {}; return executeOperation( this.s.db.s.client, new DistinctOperation( this as TODO_NODE_3286, key as TODO_NODE_3286, filter, - resolveOptions(this, options as DistinctOptions) + resolveOptions(this, options) ) ); } @@ -985,8 +986,7 @@ export class Collection { * @param filter - The filter for the count. * @param options - Optional settings for the command */ - async count(filter?: Filter, options?: CountOptions): Promise { - filter ??= {}; + async count(filter: Filter = {}, options: CountOptions = {}): Promise { return executeOperation( this.s.db.s.client, new CountOperation( diff --git a/src/cursor/find_cursor.ts b/src/cursor/find_cursor.ts index f79f52b9c36..ff4cd6ddf07 100644 --- a/src/cursor/find_cursor.ts +++ b/src/cursor/find_cursor.ts @@ -42,12 +42,12 @@ export class FindCursor extends AbstractCursor { constructor( client: MongoClient, namespace: MongoDBNamespace, - filter: Document | undefined, + filter: Document = {}, options: FindOptions = {} ) { super(client, namespace, options); - this[kFilter] = filter || {}; + this[kFilter] = filter; this[kBuiltOptions] = options; if (options.sort != null) { diff --git a/src/gridfs/index.ts b/src/gridfs/index.ts index d090626786f..1672096b6a2 100644 --- a/src/gridfs/index.ts +++ b/src/gridfs/index.ts @@ -153,9 +153,7 @@ export class GridFSBucket extends TypedEventEmitter { } /** Convenience wrapper around find on the files collection */ - find(filter?: Filter, options?: FindOptions): FindCursor { - filter ??= {}; - options = options ?? {}; + find(filter: Filter = {}, options: FindOptions = {}): FindCursor { return this.s._filesCollection.find(filter, options); } From e0b9538acc545c725f0b75bcf4e2ad895afceeae Mon Sep 17 00:00:00 2001 From: Neal Beeken Date: Sat, 21 Jan 2023 12:28:31 -0500 Subject: [PATCH 26/34] fix tests, migration --- etc/notes/CHANGES_5.0.0.md | 49 ++++++++++++++++++- test/integration/crud/misc_cursors.test.js | 6 +-- .../transactions/transactions.test.ts | 31 +++++++----- 3 files changed, 71 insertions(+), 15 deletions(-) diff --git a/etc/notes/CHANGES_5.0.0.md b/etc/notes/CHANGES_5.0.0.md index f90cebc250e..2cefc879908 100644 --- a/etc/notes/CHANGES_5.0.0.md +++ b/etc/notes/CHANGES_5.0.0.md @@ -16,9 +16,56 @@ The following is a detailed collection of the changes in the major v5 release of ## Changes +### Callback support migrated to `mongodb-legacy` + +If you are a callback user and you are not ready to use promises support for your workflow has **not** been removed. +We have migrated it to a new package: + +- [`mongodb-legacy` Github](https://github.com/mongodb-js/nodejs-mongodb-legacy#readme) +- [`mongodb-legacy` npm](https://www.npmjs.com/package/mongodb-legacy) + +The package wraps all of the driver's asynchronous operations and provides the _optional_ callback support. All the wrapped APIs offer an optional callback argument or a Promise return value so projects with mixed usage will continue to work. + +#### Example usage of equivalent callback and promise usage + +After installing the package and modifying imports the following example demonstrates equivalent usages of either `async`/`await` syntax, `.then`/`.catch` chaining, or callbacks: + +```ts +// Just add '-legacy' to my mongodb import +import { MongoClient } from 'mongodb-legacy'; +const client = new MongoClient(); +const db = client.db(); +const collection = db.collection('pets'); + +// Legacy projects may have intermixed API usage: +app.get('/endpoint_async_await', async (req, res) => { + try { + const result = await collection.findOne({}) + res.end(JSON.stringify(result)); + } catch (error) { + res.errorHandling(error) + } +}); + +app.get('/endpoint_promises', (req, res) => { + collection + .findOne({}) + .then(result => res.end(JSON.stringify(result))) + .catch(error => res.errorHandling(error)); +}); + +app.get('/endpoint_callbacks', (req, res) => { + collection.findOne({}, (error, result) => { + if (error) return res.errorHandling(error); + res.end(JSON.stringify(result)); + }); +}); +``` + + ### Dot Notation Typescript Support Removed By Default -**NOTE** This is a **Typescript compile-time only** change. Dot notation in filters sent to MongoDB will still work the same. +**NOTE:** This is a **Typescript compile-time only** change. Dot notation in filters sent to MongoDB will still work the same. Version 4.3.0 introduced Typescript support for dot notation in filter predicates. For example: diff --git a/test/integration/crud/misc_cursors.test.js b/test/integration/crud/misc_cursors.test.js index ba5b2bfe909..4cbe1691294 100644 --- a/test/integration/crud/misc_cursors.test.js +++ b/test/integration/crud/misc_cursors.test.js @@ -1671,9 +1671,9 @@ describe('Cursor', function () { } }); - it('removes session when cloning an aggregation cursor', async function () { + it('removes session when cloning an find cursor', async function () { // TODO(NODE-4988): Need to fix cursor.clone in mongodb-legacy, callbacks do not work on cloned cursor - const collection = await client.db().collection(); + const collection = await client.db().collection('test'); const cursor = collection.find({}); const clonedCursor = cursor.clone(); @@ -1685,7 +1685,7 @@ describe('Cursor', function () { it('removes session when cloning an aggregation cursor', async function () { // TODO(NODE-4988): Need to fix cursor.clone in mongodb-legacy, callbacks do not work on cloned cursor - const collection = await client.db().collection(); + const collection = await client.db().collection('test'); const cursor = collection.aggregate([{ $match: {} }]); const clonedCursor = cursor.clone(); diff --git a/test/integration/transactions/transactions.test.ts b/test/integration/transactions/transactions.test.ts index a10312f1e5e..2ee418dec1f 100644 --- a/test/integration/transactions/transactions.test.ts +++ b/test/integration/transactions/transactions.test.ts @@ -4,6 +4,7 @@ import { ClientSession, Collection, MongoClient, + MongoInvalidArgumentError, MongoNetworkError, ServerSessionPool } from '../../mongodb'; @@ -24,23 +25,31 @@ describe('Transactions', function () { await client.close(); }); - it('should provide a useful error if a Promise is not returned', { - metadata: { - requires: { topology: ['replicaset', 'sharded'], mongodb: '>=4.1.5', serverless: 'forbid' } + it( + 'should provide a useful error if a Promise is not returned', + { + requires: { + topology: ['replicaset', 'sharded'], + mongodb: '>=4.1.5', + serverless: 'forbid' + } }, - test: function (done) { - function fnThatDoesntReturnPromise() { + async function () { + function fnThatDoesNotReturnPromise() { return false; } - // @ts-expect-error: Testing that a non promise returning function is handled correctly - expect(() => session.withTransaction(fnThatDoesntReturnPromise)).to.throw( - /must return a Promise/ - ); + const result = await session + // @ts-expect-error: testing a function that does not return a promise + .withTransaction(fnThatDoesNotReturnPromise) + .catch(error => error); - session.endSession(done); + expect(result).to.be.instanceOf(MongoInvalidArgumentError); + expect(result.message).to.match(/must return a Promise/); + + await session.endSession(); } - }); + ); it('should return readable error if promise rejected with no reason', { metadata: { From 43a0dbb3c6c26bbf8542402fa666d448ad26f248 Mon Sep 17 00:00:00 2001 From: Neal Beeken Date: Sat, 21 Jan 2023 12:57:08 -0500 Subject: [PATCH 27/34] strangely close returns undefined --- src/cursor/abstract_cursor.ts | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/cursor/abstract_cursor.ts b/src/cursor/abstract_cursor.ts index 67dceec5e23..8f21216fc94 100644 --- a/src/cursor/abstract_cursor.ts +++ b/src/cursor/abstract_cursor.ts @@ -866,7 +866,11 @@ class ReadableCursorStream extends Readable { } override _destroy(error: Error | null, callback: (error?: Error | null) => void): void { - this._cursor.close().finally(() => callback(error)); + // FIXME: how is this possible? + const res = this._cursor.close(); + if (res != null) { + res.finally(() => callback(error)); + } } private _readNext() { From 1ee4f9afc5a5caaffdb1a094321fc69304f3ef58 Mon Sep 17 00:00:00 2001 From: Neal Beeken Date: Sat, 21 Jan 2023 13:06:33 -0500 Subject: [PATCH 28/34] fix test --- test/integration/crud/find_and_modify.test.js | 20 +++++++++---------- 1 file changed, 9 insertions(+), 11 deletions(-) diff --git a/test/integration/crud/find_and_modify.test.js b/test/integration/crud/find_and_modify.test.js index c2e36073842..2735d0a95e4 100644 --- a/test/integration/crud/find_and_modify.test.js +++ b/test/integration/crud/find_and_modify.test.js @@ -308,16 +308,14 @@ describe('Find and Modify', function () { } }); - it('should not allow atomic operators for findOneAndReplace', { - metadata: { requires: { topology: 'single' } }, - test: async function () { - const client = this.configuration.newClient(); - const db = client.db('fakeDb'); - const collection = db.collection('test'); - expect(() => { - collection.findOneAndReplace({ a: 1 }, { $set: { a: 14 } }); - }).to.throw(/must not contain atomic operators/); - await client.close(); - } + it('should not allow atomic operators for findOneAndReplace', async function () { + const client = this.configuration.newClient(); + const db = client.db('fakeDb'); + const collection = db.collection('test'); + const error = await collection + .findOneAndReplace({ a: 1 }, { $set: { a: 14 } }) + .catch(error => error); + expect(error.message).to.match(/must not contain atomic operators/); + await client.close(); }); }); From a11d1757d508f264e7348a4747c00f8388b0613d Mon Sep 17 00:00:00 2001 From: Neal Beeken Date: Sat, 21 Jan 2023 13:37:56 -0500 Subject: [PATCH 29/34] argument defaulting fix --- src/collection.ts | 31 ++++---- .../node-specific/operation_examples.test.ts | 70 +++++++------------ 2 files changed, 41 insertions(+), 60 deletions(-) diff --git a/src/collection.ts b/src/collection.ts index 616ba80fdf3..410aaed6419 100644 --- a/src/collection.ts +++ b/src/collection.ts @@ -402,7 +402,10 @@ export class Collection { * @param filter - The filter used to select the document to remove * @param options - Optional settings for the command */ - async deleteOne(filter: Filter, options?: DeleteOptions): Promise { + async deleteOne( + filter: Filter = {}, + options: DeleteOptions = {} + ): Promise { return executeOperation( this.s.db.s.client, new DeleteOneOperation(this as TODO_NODE_3286, filter, resolveOptions(this, options)) @@ -415,7 +418,10 @@ export class Collection { * @param filter - The filter used to select the documents to remove * @param options - Optional settings for the command */ - async deleteMany(filter: Filter, options?: DeleteOptions): Promise { + async deleteMany( + filter: Filter = {}, + options: DeleteOptions = {} + ): Promise { return executeOperation( this.s.db.s.client, new DeleteManyOperation(this as TODO_NODE_3286, filter, resolveOptions(this, options)) @@ -469,11 +475,11 @@ export class Collection { async findOne(filter: Filter): Promise; async findOne(filter: Filter, options?: FindOptions): Promise; - async findOne(filter?: Filter, options?: FindOptions): Promise | null> { - return this.find(filter ?? {}, options ?? {}) - .limit(-1) - .batchSize(1) - .next(); + async findOne( + filter: Filter = {}, + options: FindOptions = {} + ): Promise | null> { + return this.find(filter, options).limit(-1).batchSize(1).next(); } /** @@ -484,16 +490,7 @@ export class Collection { find(): FindCursor>; find(filter: Filter, options?: FindOptions): FindCursor>; find(filter: Filter, options?: FindOptions): FindCursor; - find(filter?: Filter, options?: FindOptions): FindCursor> { - if (arguments.length > 2) { - throw new MongoInvalidArgumentError( - 'Method "collection.find()" accepts at most two arguments' - ); - } - if (typeof options === 'function') { - throw new MongoInvalidArgumentError('Argument "options" must not be function'); - } - + find(filter: Filter = {}, options: FindOptions = {}): FindCursor> { return new FindCursor>( this.s.db.s.client, this.s.namespace, diff --git a/test/integration/node-specific/operation_examples.test.ts b/test/integration/node-specific/operation_examples.test.ts index eb092ce853f..10e42492f46 100644 --- a/test/integration/node-specific/operation_examples.test.ts +++ b/test/integration/node-specific/operation_examples.test.ts @@ -1,13 +1,13 @@ import { expect } from 'chai'; import { format as f } from 'util'; -import { Code, enumToString, ProfilingLevel, ReturnDocument } from '../../mongodb'; +import { Code, enumToString, MongoClient, ProfilingLevel, ReturnDocument } from '../../mongodb'; import { skipBrokenAuthTestBeforeEachHook } from '../../tools/runner/hooks/configuration'; import { sleep as delay } from '../../tools/utils'; import { setupDatabase } from '../shared'; describe('Operations', function () { - let client; + let client: MongoClient; beforeEach(async function () { client = this.configuration.newClient(); }); @@ -1572,48 +1572,32 @@ describe('Operations', function () { * example-class Collection * example-method remove */ - it('shouldRemoveAllDocumentsNoSafeWithPromises', { - metadata: { requires: { topology: ['single'] } }, - - test: function () { - const configuration = this.configuration; - const client = configuration.newClient({ maxPoolSize: 1 }); - - return client.connect().then(function (client) { - const db = client.db(configuration.db); - // LINE var MongoClient = require('mongodb').MongoClient, - // LINE test = require('assert'); - // LINE const client = new MongoClient('mongodb://localhost:27017/test'); - // LINE client.connect().then(() => { - // LINE var db = client.db('test); - // REPLACE configuration.writeConcernMax() WITH {w:1} - // REMOVE-LINE done(); - // BEGIN - - // Fetch a collection to insert document into - const collection = db.collection('remove_all_documents_no_safe_with_promise'); - - // Insert a bunch of documents - return collection - .insertMany([{ a: 1 }, { b: 2 }], { writeConcern: { w: 1 } }) - .then(function (result) { - expect(result).to.exist; - - // Remove all the document - return collection.deleteMany(); - }) - .then(function () { - // Fetch all results - return collection.find().toArray(); - }) - .then(function (items) { - expect(items.length).to.equal(0); - return client.close(); - }); - }); - // END + it( + 'deleteMany() deletes all documents in collection', + { requires: { topology: ['single'] } }, + function () { + const db = client.db(); + // Fetch a collection to insert document into + const collection = db.collection('remove_all_documents_no_safe_with_promise'); + + // Insert a bunch of documents + return collection + .insertMany([{ a: 1 }, { b: 2 }], { writeConcern: { w: 1 } }) + .then(function (result) { + expect(result).to.exist; + // Remove all the document + return collection.deleteMany(); + }) + .then(function () { + // Fetch all results + return collection.find().toArray(); + }) + .then(function (items) { + expect(items.length).to.equal(0); + return client.close(); + }); } - }); + ); /** * An example removing a subset of documents using safe mode to ensure removal of documents using a Promise. From cad280488498b95518106c7879cd077605dbd30a Mon Sep 17 00:00:00 2001 From: Neal Beeken Date: Sat, 21 Jan 2023 13:39:34 -0500 Subject: [PATCH 30/34] fixup test --- .../node-specific/operation_examples.test.ts | 38 ++++++------------- 1 file changed, 12 insertions(+), 26 deletions(-) diff --git a/test/integration/node-specific/operation_examples.test.ts b/test/integration/node-specific/operation_examples.test.ts index 10e42492f46..b0de3fc1276 100644 --- a/test/integration/node-specific/operation_examples.test.ts +++ b/test/integration/node-specific/operation_examples.test.ts @@ -1572,32 +1572,18 @@ describe('Operations', function () { * example-class Collection * example-method remove */ - it( - 'deleteMany() deletes all documents in collection', - { requires: { topology: ['single'] } }, - function () { - const db = client.db(); - // Fetch a collection to insert document into - const collection = db.collection('remove_all_documents_no_safe_with_promise'); - - // Insert a bunch of documents - return collection - .insertMany([{ a: 1 }, { b: 2 }], { writeConcern: { w: 1 } }) - .then(function (result) { - expect(result).to.exist; - // Remove all the document - return collection.deleteMany(); - }) - .then(function () { - // Fetch all results - return collection.find().toArray(); - }) - .then(function (items) { - expect(items.length).to.equal(0); - return client.close(); - }); - } - ); + it('deleteMany() deletes all documents in collection', async function () { + const db = client.db(); + // Fetch a collection to insert document into + const collection = db.collection('remove_all_documents_no_safe_with_promise'); + + // Insert a bunch of documents + const result = await collection.insertMany([{ a: 1 }, { b: 2 }], { writeConcern: { w: 1 } }); + expect(result).to.exist; + await collection.deleteMany(); + const items = await collection.find().toArray(); + expect(items).to.have.lengthOf(0); + }); /** * An example removing a subset of documents using safe mode to ensure removal of documents using a Promise. From 5b7dc71e06372ebcd9bbf824b9cab1515c33bc66 Mon Sep 17 00:00:00 2001 From: Neal Beeken Date: Sat, 21 Jan 2023 21:04:45 -0500 Subject: [PATCH 31/34] fix async function strangeness --- src/cursor/abstract_cursor.ts | 6 +----- .../integration/change-streams/change_streams.prose.test.ts | 4 ++-- 2 files changed, 3 insertions(+), 7 deletions(-) diff --git a/src/cursor/abstract_cursor.ts b/src/cursor/abstract_cursor.ts index e6bd3737b05..444ed14e1fe 100644 --- a/src/cursor/abstract_cursor.ts +++ b/src/cursor/abstract_cursor.ts @@ -866,11 +866,7 @@ class ReadableCursorStream extends Readable { } override _destroy(error: Error | null, callback: (error?: Error | null) => void): void { - // FIXME: how is this possible? - const res = this._cursor.close(); - if (res != null) { - res.finally(() => callback(error)); - } + this._cursor.close().finally(() => callback(error)); } private _readNext() { diff --git a/test/integration/change-streams/change_streams.prose.test.ts b/test/integration/change-streams/change_streams.prose.test.ts index 531776adeb3..152b89fbc51 100644 --- a/test/integration/change-streams/change_streams.prose.test.ts +++ b/test/integration/change-streams/change_streams.prose.test.ts @@ -37,7 +37,7 @@ function triggerResumableError( } const stub = sinon.stub(changeStream.cursor, 'close'); - stub.callsFake(function () { + stub.callsFake(async function () { stub.wrappedMethod.call(this); stub.restore(); onClose(); @@ -50,7 +50,7 @@ function triggerResumableError( return; } - const nextStub = sinon.stub(changeStream.cursor, 'next').callsFake(function (callback) { + const nextStub = sinon.stub(changeStream.cursor, 'next').callsFake(async function () { callback(new MongoNetworkError('error triggered from test')); nextStub.restore(); }); From b489aca375b8c21058e8c48c5527782248941592 Mon Sep 17 00:00:00 2001 From: Neal Beeken Date: Mon, 23 Jan 2023 10:18:22 -0500 Subject: [PATCH 32/34] handle close error --- src/cursor/abstract_cursor.ts | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/cursor/abstract_cursor.ts b/src/cursor/abstract_cursor.ts index 444ed14e1fe..2d9ec01a3dd 100644 --- a/src/cursor/abstract_cursor.ts +++ b/src/cursor/abstract_cursor.ts @@ -866,7 +866,10 @@ class ReadableCursorStream extends Readable { } override _destroy(error: Error | null, callback: (error?: Error | null) => void): void { - this._cursor.close().finally(() => callback(error)); + this._cursor.close().then( + () => callback(error), + closeError => callback(closeError) + ); } private _readNext() { From c0b36a95783a335c66b870af3a59e2bac8527036 Mon Sep 17 00:00:00 2001 From: Neal Beeken Date: Mon, 23 Jan 2023 15:24:11 -0500 Subject: [PATCH 33/34] migration suggestions Co-authored-by: Daria Pardue --- etc/notes/CHANGES_5.0.0.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/etc/notes/CHANGES_5.0.0.md b/etc/notes/CHANGES_5.0.0.md index 8eb07efdd95..b19d447283a 100644 --- a/etc/notes/CHANGES_5.0.0.md +++ b/etc/notes/CHANGES_5.0.0.md @@ -16,15 +16,15 @@ The following is a detailed collection of the changes in the major v5 release of ## Changes -### Callback support migrated to `mongodb-legacy` +### Optional callback support migrated to `mongodb-legacy` -If you are a callback user and you are not ready to use promises support for your workflow has **not** been removed. +If you are a callback user and you are not ready to use promises, support for your workflow has **not** been removed. We have migrated it to a new package: - [`mongodb-legacy` Github](https://github.com/mongodb-js/nodejs-mongodb-legacy#readme) - [`mongodb-legacy` npm](https://www.npmjs.com/package/mongodb-legacy) -The package wraps all of the driver's asynchronous operations and provides the _optional_ callback support. All the wrapped APIs offer an optional callback argument or a Promise return value so projects with mixed usage will continue to work. +The package wraps all of the driver's asynchronous operations that previously supported both promises and callbacks. All the wrapped APIs offer callback support via an optional callback argument alongside a Promise return value so projects with mixed usage will continue to work. #### Example usage of equivalent callback and promise usage From b91886ccaad7234ed613118230f0204995a19d6a Mon Sep 17 00:00:00 2001 From: Neal Beeken Date: Mon, 23 Jan 2023 15:25:26 -0500 Subject: [PATCH 34/34] rm todos --- test/integration/crud/find_cursor_methods.test.js | 2 -- test/integration/crud/misc_cursors.test.js | 2 -- 2 files changed, 4 deletions(-) diff --git a/test/integration/crud/find_cursor_methods.test.js b/test/integration/crud/find_cursor_methods.test.js index 69324efaa04..9ead78b794c 100644 --- a/test/integration/crud/find_cursor_methods.test.js +++ b/test/integration/crud/find_cursor_methods.test.js @@ -206,7 +206,6 @@ describe('Find Cursor', function () { context('#clone', function () { it('should clone a find cursor', async function () { - // TODO(NODE-4988): Need to fix cursor.clone in mongodb-legacy, callbacks do not work on cloned cursor const coll = client.db().collection('abstract_cursor'); const cursor = coll.find({}); @@ -221,7 +220,6 @@ describe('Find Cursor', function () { }); it('should clone an aggregate cursor', async function () { - // TODO(NODE-4988): Need to fix cursor.clone in mongodb-legacy, callbacks do not work on cloned cursor const coll = client.db().collection('abstract_cursor'); const cursor = coll.aggregate([{ $match: {} }]); diff --git a/test/integration/crud/misc_cursors.test.js b/test/integration/crud/misc_cursors.test.js index 4cbe1691294..e0cc7a77d59 100644 --- a/test/integration/crud/misc_cursors.test.js +++ b/test/integration/crud/misc_cursors.test.js @@ -1672,7 +1672,6 @@ describe('Cursor', function () { }); it('removes session when cloning an find cursor', async function () { - // TODO(NODE-4988): Need to fix cursor.clone in mongodb-legacy, callbacks do not work on cloned cursor const collection = await client.db().collection('test'); const cursor = collection.find({}); @@ -1684,7 +1683,6 @@ describe('Cursor', function () { }); it('removes session when cloning an aggregation cursor', async function () { - // TODO(NODE-4988): Need to fix cursor.clone in mongodb-legacy, callbacks do not work on cloned cursor const collection = await client.db().collection('test'); const cursor = collection.aggregate([{ $match: {} }]);