From 77a2709a47446bb3bae59d9c38b4d952433867a2 Mon Sep 17 00:00:00 2001 From: malikj2000 <67246038+malikj2000@users.noreply.github.com> Date: Fri, 7 Jul 2023 15:04:47 -0400 Subject: [PATCH] refactor(NODE-5360): refactor CommandOperation to use async (#3749) --- src/index.ts | 1 + src/operations/add_user.ts | 6 +- src/operations/aggregate.ts | 10 +++- src/operations/command.ts | 28 +++++++-- src/operations/count.ts | 6 +- src/operations/create_collection.ts | 6 +- src/operations/delete.ts | 10 +++- src/operations/distinct.ts | 6 +- src/operations/drop.ts | 10 ++-- src/operations/estimated_document_count.ts | 6 +- src/operations/eval.ts | 6 +- src/operations/find.ts | 8 ++- src/operations/find_and_modify.ts | 6 +- src/operations/indexes.ts | 14 ++--- src/operations/insert.ts | 6 +- src/operations/list_collections.ts | 6 +- src/operations/list_databases.ts | 6 +- src/operations/profiling_level.ts | 6 +- src/operations/remove_user.ts | 6 +- src/operations/run_command.ts | 6 +- src/operations/set_profiling_level.ts | 6 +- src/operations/stats.ts | 10 ++-- src/operations/update.ts | 10 +++- src/operations/validate_collection.ts | 6 +- src/sdam/server.ts | 9 +-- test/unit/operations/abstract_command.test.ts | 57 +++++++++++++++++++ 26 files changed, 173 insertions(+), 84 deletions(-) create mode 100644 test/unit/operations/abstract_command.test.ts diff --git a/src/index.ts b/src/index.ts index daf30704a1..edcc43610d 100644 --- a/src/index.ts +++ b/src/index.ts @@ -388,6 +388,7 @@ export type { CommandOperationOptions, OperationParent } from './operations/command'; +export type { CommandCallbackOperation } from './operations/command'; export type { IndexInformationOptions } from './operations/common_functions'; export type { CountOptions } from './operations/count'; export type { CountDocumentsOptions } from './operations/count_documents'; diff --git a/src/operations/add_user.ts b/src/operations/add_user.ts index b60381f6d4..21a4bbc8c9 100644 --- a/src/operations/add_user.ts +++ b/src/operations/add_user.ts @@ -6,7 +6,7 @@ import { MongoInvalidArgumentError } from '../error'; import type { Server } from '../sdam/server'; import type { ClientSession } from '../sessions'; import { type Callback, emitWarningOnce, getTopology } from '../utils'; -import { CommandOperation, type CommandOperationOptions } from './command'; +import { CommandCallbackOperation, type CommandOperationOptions } from './command'; import { Aspect, defineAspects } from './operation'; /** @@ -35,7 +35,7 @@ export interface AddUserOptions extends CommandOperationOptions { } /** @internal */ -export class AddUserOperation extends CommandOperation { +export class AddUserOperation extends CommandCallbackOperation { override options: AddUserOptions; db: Db; username: string; @@ -117,7 +117,7 @@ export class AddUserOperation extends CommandOperation { command.pwd = userPassword; } - super.executeCommand(server, session, command, callback); + super.executeCommandCallback(server, session, command, callback); } } diff --git a/src/operations/aggregate.ts b/src/operations/aggregate.ts index a742b6ce9f..47e19a4d8e 100644 --- a/src/operations/aggregate.ts +++ b/src/operations/aggregate.ts @@ -4,7 +4,11 @@ import type { Server } from '../sdam/server'; import type { ClientSession } from '../sessions'; import { type Callback, maxWireVersion, type MongoDBNamespace } from '../utils'; import { WriteConcern } from '../write_concern'; -import { type CollationOptions, CommandOperation, type CommandOperationOptions } from './command'; +import { + type CollationOptions, + CommandCallbackOperation, + type CommandOperationOptions +} from './command'; import { Aspect, defineAspects, type Hint } from './operation'; /** @internal */ @@ -36,7 +40,7 @@ export interface AggregateOptions extends CommandOperationOptions { } /** @internal */ -export class AggregateOperation extends CommandOperation { +export class AggregateOperation extends CommandCallbackOperation { override options: AggregateOptions; target: string | typeof DB_AGGREGATE_COLLECTION; pipeline: Document[]; @@ -133,7 +137,7 @@ export class AggregateOperation extends CommandOperation { command.cursor.batchSize = options.batchSize; } - super.executeCommand(server, session, command, callback); + super.executeCommandCallback(server, session, command, callback); } } diff --git a/src/operations/command.ts b/src/operations/command.ts index 7880ad95a2..f6ec6cbe02 100644 --- a/src/operations/command.ts +++ b/src/operations/command.ts @@ -107,12 +107,11 @@ export abstract class CommandOperation extends AbstractCallbackOperation { return true; } - executeCommand( + async executeCommand( server: Server, session: ClientSession | undefined, - cmd: Document, - callback: Callback - ): void { + cmd: Document + ): Promise { // TODO: consider making this a non-enumerable property this.server = server; @@ -154,6 +153,25 @@ export abstract class CommandOperation extends AbstractCallbackOperation { cmd = decorateWithExplain(cmd, this.explain); } - server.command(this.ns, cmd, options, callback); + return server.commandAsync(this.ns, cmd, options); + } +} + +/** @internal */ +export abstract class CommandCallbackOperation extends CommandOperation { + constructor(parent?: OperationParent, options?: CommandOperationOptions) { + super(parent, options); + } + + executeCommandCallback( + server: Server, + session: ClientSession | undefined, + cmd: Document, + callback: Callback + ): void { + super.executeCommand(server, session, cmd).then( + res => callback(undefined, res), + err => callback(err, undefined) + ); } } diff --git a/src/operations/count.ts b/src/operations/count.ts index 0b21bdd359..9a6da8eadd 100644 --- a/src/operations/count.ts +++ b/src/operations/count.ts @@ -3,7 +3,7 @@ import type { Collection } from '../collection'; import type { Server } from '../sdam/server'; import type { ClientSession } from '../sessions'; import type { Callback, MongoDBNamespace } from '../utils'; -import { CommandOperation, type CommandOperationOptions } from './command'; +import { CommandCallbackOperation, type CommandOperationOptions } from './command'; import { Aspect, defineAspects } from './operation'; /** @public */ @@ -19,7 +19,7 @@ export interface CountOptions extends CommandOperationOptions { } /** @internal */ -export class CountOperation extends CommandOperation { +export class CountOperation extends CommandCallbackOperation { override options: CountOptions; collectionName?: string; query: Document; @@ -59,7 +59,7 @@ export class CountOperation extends CommandOperation { cmd.maxTimeMS = options.maxTimeMS; } - super.executeCommand(server, session, cmd, (err, result) => { + super.executeCommandCallback(server, session, cmd, (err, result) => { callback(err, result ? result.n : 0); }); } diff --git a/src/operations/create_collection.ts b/src/operations/create_collection.ts index ff28e77dd0..9c5a668b79 100644 --- a/src/operations/create_collection.ts +++ b/src/operations/create_collection.ts @@ -10,7 +10,7 @@ import type { PkFactory } from '../mongo_client'; import type { Server } from '../sdam/server'; import type { ClientSession } from '../sessions'; import type { Callback } from '../utils'; -import { CommandOperation, type CommandOperationOptions } from './command'; +import { CommandCallbackOperation, type CommandOperationOptions } from './command'; import { CreateIndexOperation } from './indexes'; import { Aspect, defineAspects } from './operation'; @@ -108,7 +108,7 @@ const INVALID_QE_VERSION = 'Driver support of Queryable Encryption is incompatible with server. Upgrade server to use Queryable Encryption.'; /** @internal */ -export class CreateCollectionOperation extends CommandOperation { +export class CreateCollectionOperation extends CommandCallbackOperation { override options: CreateCollectionOptions; db: Db; name: string; @@ -209,7 +209,7 @@ export class CreateCollectionOperation extends CommandOperation { } // otherwise just execute the command - super.executeCommand(server, session, cmd, done); + super.executeCommandCallback(server, session, cmd, done); }); } } diff --git a/src/operations/delete.ts b/src/operations/delete.ts index c57658e237..ba7e6b19ee 100644 --- a/src/operations/delete.ts +++ b/src/operations/delete.ts @@ -5,7 +5,11 @@ import type { Server } from '../sdam/server'; import type { ClientSession } from '../sessions'; import type { Callback, MongoDBNamespace } from '../utils'; import type { WriteConcernOptions } from '../write_concern'; -import { type CollationOptions, CommandOperation, type CommandOperationOptions } from './command'; +import { + type CollationOptions, + CommandCallbackOperation, + type CommandOperationOptions +} from './command'; import { Aspect, defineAspects, type Hint } from './operation'; /** @public */ @@ -41,7 +45,7 @@ export interface DeleteStatement { } /** @internal */ -export class DeleteOperation extends CommandOperation { +export class DeleteOperation extends CommandCallbackOperation { override options: DeleteOptions; statements: DeleteStatement[]; @@ -92,7 +96,7 @@ export class DeleteOperation extends CommandOperation { } } - super.executeCommand(server, session, command, callback); + super.executeCommandCallback(server, session, command, callback); } } diff --git a/src/operations/distinct.ts b/src/operations/distinct.ts index 6468074b15..327b768734 100644 --- a/src/operations/distinct.ts +++ b/src/operations/distinct.ts @@ -3,7 +3,7 @@ import type { Collection } from '../collection'; import type { Server } from '../sdam/server'; import type { ClientSession } from '../sessions'; import { type Callback, decorateWithCollation, decorateWithReadConcern } from '../utils'; -import { CommandOperation, type CommandOperationOptions } from './command'; +import { CommandCallbackOperation, type CommandOperationOptions } from './command'; import { Aspect, defineAspects } from './operation'; /** @public */ @@ -13,7 +13,7 @@ export type DistinctOptions = CommandOperationOptions; * Return a list of distinct values for the given key across a collection. * @internal */ -export class DistinctOperation extends CommandOperation { +export class DistinctOperation extends CommandCallbackOperation { override options: DistinctOptions; collection: Collection; /** Field of the document to find distinct values for. */ @@ -76,7 +76,7 @@ export class DistinctOperation extends CommandOperation { return callback(err); } - super.executeCommand(server, session, cmd, (err, result) => { + super.executeCommandCallback(server, session, cmd, (err, result) => { if (err) { callback(err); return; diff --git a/src/operations/drop.ts b/src/operations/drop.ts index 69e2e69826..5c71b15215 100644 --- a/src/operations/drop.ts +++ b/src/operations/drop.ts @@ -4,7 +4,7 @@ import { MONGODB_ERROR_CODES, MongoServerError } from '../error'; import type { Server } from '../sdam/server'; import type { ClientSession } from '../sessions'; import type { Callback } from '../utils'; -import { CommandOperation, type CommandOperationOptions } from './command'; +import { CommandCallbackOperation, type CommandOperationOptions } from './command'; import { Aspect, defineAspects } from './operation'; /** @public */ @@ -14,7 +14,7 @@ export interface DropCollectionOptions extends CommandOperationOptions { } /** @internal */ -export class DropCollectionOperation extends CommandOperation { +export class DropCollectionOperation extends CommandCallbackOperation { override options: DropCollectionOptions; db: Db; name: string; @@ -83,7 +83,7 @@ export class DropCollectionOperation extends CommandOperation { session: ClientSession | undefined ): Promise { return new Promise((resolve, reject) => { - super.executeCommand(server, session, { drop: this.name }, (err, result) => { + super.executeCommandCallback(server, session, { drop: this.name }, (err, result) => { if (err) return reject(err); resolve(!!result.ok); }); @@ -95,7 +95,7 @@ export class DropCollectionOperation extends CommandOperation { export type DropDatabaseOptions = CommandOperationOptions; /** @internal */ -export class DropDatabaseOperation extends CommandOperation { +export class DropDatabaseOperation extends CommandCallbackOperation { override options: DropDatabaseOptions; constructor(db: Db, options: DropDatabaseOptions) { @@ -107,7 +107,7 @@ export class DropDatabaseOperation extends CommandOperation { session: ClientSession | undefined, callback: Callback ): void { - super.executeCommand(server, session, { dropDatabase: 1 }, (err, result) => { + super.executeCommandCallback(server, session, { dropDatabase: 1 }, (err, result) => { if (err) return callback(err); if (result.ok) return callback(undefined, true); callback(undefined, false); diff --git a/src/operations/estimated_document_count.ts b/src/operations/estimated_document_count.ts index 8a9048bbaa..c2195af77a 100644 --- a/src/operations/estimated_document_count.ts +++ b/src/operations/estimated_document_count.ts @@ -3,7 +3,7 @@ import type { Collection } from '../collection'; import type { Server } from '../sdam/server'; import type { ClientSession } from '../sessions'; import type { Callback } from '../utils'; -import { CommandOperation, type CommandOperationOptions } from './command'; +import { CommandCallbackOperation, type CommandOperationOptions } from './command'; import { Aspect, defineAspects } from './operation'; /** @public */ @@ -17,7 +17,7 @@ export interface EstimatedDocumentCountOptions extends CommandOperationOptions { } /** @internal */ -export class EstimatedDocumentCountOperation extends CommandOperation { +export class EstimatedDocumentCountOperation extends CommandCallbackOperation { override options: EstimatedDocumentCountOptions; collectionName: string; @@ -44,7 +44,7 @@ export class EstimatedDocumentCountOperation extends CommandOperation { cmd.comment = this.options.comment; } - super.executeCommand(server, session, cmd, (err, response) => { + super.executeCommandCallback(server, session, cmd, (err, response) => { if (err) { callback(err); return; diff --git a/src/operations/eval.ts b/src/operations/eval.ts index 1f9b5ebbc4..a32a618891 100644 --- a/src/operations/eval.ts +++ b/src/operations/eval.ts @@ -6,7 +6,7 @@ import { ReadPreference } from '../read_preference'; import type { Server } from '../sdam/server'; import type { ClientSession } from '../sessions'; import type { Callback } from '../utils'; -import { CommandOperation, type CommandOperationOptions } from './command'; +import { CommandCallbackOperation, type CommandOperationOptions } from './command'; /** @public */ export interface EvalOptions extends CommandOperationOptions { @@ -14,7 +14,7 @@ export interface EvalOptions extends CommandOperationOptions { } /** @internal */ -export class EvalOperation extends CommandOperation { +export class EvalOperation extends CommandCallbackOperation { override options: EvalOptions; code: Code; parameters?: Document | Document[]; @@ -65,7 +65,7 @@ export class EvalOperation extends CommandOperation { } // Execute the command - super.executeCommand(server, session, cmd, (err, result) => { + super.executeCommandCallback(server, session, cmd, (err, result) => { if (err) return callback(err); if (result && result.ok === 1) { return callback(undefined, result.retval); diff --git a/src/operations/find.ts b/src/operations/find.ts index 2d2da710e1..fd3d41e8c8 100644 --- a/src/operations/find.ts +++ b/src/operations/find.ts @@ -11,7 +11,11 @@ import { type MongoDBNamespace, normalizeHintField } from '../utils'; -import { type CollationOptions, CommandOperation, type CommandOperationOptions } from './command'; +import { + type CollationOptions, + CommandCallbackOperation, + type CommandOperationOptions +} from './command'; import { Aspect, defineAspects, type Hint } from './operation'; /** @@ -71,7 +75,7 @@ export interface FindOptions } /** @internal */ -export class FindOperation extends CommandOperation { +export class FindOperation extends CommandCallbackOperation { /** * @remarks WriteConcern can still be present on the options because * we inherit options from the client/db/collection. The diff --git a/src/operations/find_and_modify.ts b/src/operations/find_and_modify.ts index a9dca92d79..d92d6efbd8 100644 --- a/src/operations/find_and_modify.ts +++ b/src/operations/find_and_modify.ts @@ -7,7 +7,7 @@ import type { ClientSession } from '../sessions'; import { formatSort, type Sort, type SortForCmd } from '../sort'; import { type Callback, decorateWithCollation, hasAtomicOperators, maxWireVersion } from '../utils'; import type { WriteConcern, WriteConcernSettings } from '../write_concern'; -import { CommandOperation, type CommandOperationOptions } from './command'; +import { CommandCallbackOperation, type CommandOperationOptions } from './command'; import { Aspect, defineAspects } from './operation'; /** @public */ @@ -122,7 +122,7 @@ function configureFindAndModifyCmdBaseUpdateOpts( } /** @internal */ -class FindAndModifyOperation extends CommandOperation { +class FindAndModifyOperation extends CommandCallbackOperation { override options: FindOneAndReplaceOptions | FindOneAndUpdateOptions | FindOneAndDeleteOptions; cmdBase: FindAndModifyCmdBase; collection: Collection; @@ -220,7 +220,7 @@ class FindAndModifyOperation extends CommandOperation { } // Execute the command - super.executeCommand(server, session, cmd, (err, result) => { + super.executeCommandCallback(server, session, cmd, (err, result) => { if (err) return callback(err); return callback(undefined, options.includeResultMetadata ? result : result.value ?? null); }); diff --git a/src/operations/indexes.ts b/src/operations/indexes.ts index 25542c9807..93b68f0718 100644 --- a/src/operations/indexes.ts +++ b/src/operations/indexes.ts @@ -9,7 +9,7 @@ import type { ClientSession } from '../sessions'; import { type Callback, isObject, maxWireVersion, type MongoDBNamespace } from '../utils'; import { type CollationOptions, - CommandOperation, + CommandCallbackOperation, type CommandOperationOptions, type OperationParent } from './command'; @@ -206,7 +206,7 @@ export class IndexesOperation extends AbstractCallbackOperation { /** @internal */ export class CreateIndexesOperation< T extends string | string[] = string[] -> extends CommandOperation { +> extends CommandCallbackOperation { override options: CreateIndexesOptions; collectionName: string; indexes: ReadonlyArray & { key: Map }>; @@ -266,7 +266,7 @@ export class CreateIndexesOperation< // collation is set on each index, it should not be defined at the root this.options.collation = undefined; - super.executeCommand(server, session, cmd, err => { + super.executeCommandCallback(server, session, cmd, err => { if (err) { callback(err); return; @@ -348,7 +348,7 @@ export class EnsureIndexOperation extends CreateIndexOperation { export type DropIndexesOptions = CommandOperationOptions; /** @internal */ -export class DropIndexOperation extends CommandOperation { +export class DropIndexOperation extends CommandCallbackOperation { override options: DropIndexesOptions; collection: Collection; indexName: string; @@ -367,7 +367,7 @@ export class DropIndexOperation extends CommandOperation { callback: Callback ): void { const cmd = { dropIndexes: this.collection.collectionName, index: this.indexName }; - super.executeCommand(server, session, cmd, callback); + super.executeCommandCallback(server, session, cmd, callback); } } @@ -396,7 +396,7 @@ export interface ListIndexesOptions extends Omit { +export class ListIndexesOperation extends CommandCallbackOperation { /** * @remarks WriteConcern can still be present on the options because * we inherit options from the client/db/collection. The @@ -432,7 +432,7 @@ export class ListIndexesOperation extends CommandOperation { command.comment = this.options.comment; } - super.executeCommand(server, session, command, callback); + super.executeCommandCallback(server, session, command, callback); } } diff --git a/src/operations/insert.ts b/src/operations/insert.ts index ed208cad2e..7ab61ac309 100644 --- a/src/operations/insert.ts +++ b/src/operations/insert.ts @@ -8,12 +8,12 @@ import type { ClientSession } from '../sessions'; import type { Callback, MongoDBNamespace } from '../utils'; import { WriteConcern } from '../write_concern'; import { BulkWriteOperation } from './bulk_write'; -import { CommandOperation, type CommandOperationOptions } from './command'; +import { CommandCallbackOperation, type CommandOperationOptions } from './command'; import { prepareDocs } from './common_functions'; import { AbstractCallbackOperation, Aspect, defineAspects } from './operation'; /** @internal */ -export class InsertOperation extends CommandOperation { +export class InsertOperation extends CommandCallbackOperation { override options: BulkWriteOptions; documents: Document[]; @@ -47,7 +47,7 @@ export class InsertOperation extends CommandOperation { command.comment = options.comment; } - super.executeCommand(server, session, command, callback); + super.executeCommandCallback(server, session, command, callback); } } diff --git a/src/operations/list_collections.ts b/src/operations/list_collections.ts index 339380524a..dddd4db274 100644 --- a/src/operations/list_collections.ts +++ b/src/operations/list_collections.ts @@ -3,7 +3,7 @@ import type { Db } from '../db'; import type { Server } from '../sdam/server'; import type { ClientSession } from '../sessions'; import { type Callback, maxWireVersion } from '../utils'; -import { CommandOperation, type CommandOperationOptions } from './command'; +import { CommandCallbackOperation, type CommandOperationOptions } from './command'; import { Aspect, defineAspects } from './operation'; /** @public */ @@ -17,7 +17,7 @@ export interface ListCollectionsOptions extends Omit { +export class ListCollectionsOperation extends CommandCallbackOperation { /** * @remarks WriteConcern can still be present on the options because * we inherit options from the client/db/collection. The @@ -52,7 +52,7 @@ export class ListCollectionsOperation extends CommandOperation { session: ClientSession | undefined, callback: Callback ): void { - return super.executeCommand( + return super.executeCommandCallback( server, session, this.generateCommand(maxWireVersion(server)), diff --git a/src/operations/list_databases.ts b/src/operations/list_databases.ts index 6979f566e3..8605e0661a 100644 --- a/src/operations/list_databases.ts +++ b/src/operations/list_databases.ts @@ -3,7 +3,7 @@ import type { Db } from '../db'; import type { Server } from '../sdam/server'; import type { ClientSession } from '../sessions'; import { type Callback, maxWireVersion, MongoDBNamespace } from '../utils'; -import { CommandOperation, type CommandOperationOptions } from './command'; +import { CommandCallbackOperation, type CommandOperationOptions } from './command'; import { Aspect, defineAspects } from './operation'; /** @public */ @@ -25,7 +25,7 @@ export interface ListDatabasesOptions extends CommandOperationOptions { } /** @internal */ -export class ListDatabasesOperation extends CommandOperation { +export class ListDatabasesOperation extends CommandCallbackOperation { override options: ListDatabasesOptions; constructor(db: Db, options?: ListDatabasesOptions) { @@ -59,7 +59,7 @@ export class ListDatabasesOperation extends CommandOperation { +export class ProfilingLevelOperation extends CommandCallbackOperation { override options: ProfilingLevelOptions; constructor(db: Db, options: ProfilingLevelOptions) { @@ -22,7 +22,7 @@ export class ProfilingLevelOperation extends CommandOperation { session: ClientSession | undefined, callback: Callback ): void { - super.executeCommand(server, session, { profile: -1 }, (err, doc) => { + super.executeCommandCallback(server, session, { profile: -1 }, (err, doc) => { if (err == null && doc.ok === 1) { const was = doc.was; if (was === 0) return callback(undefined, 'off'); diff --git a/src/operations/remove_user.ts b/src/operations/remove_user.ts index 1da6ebd6cd..e742fc42ee 100644 --- a/src/operations/remove_user.ts +++ b/src/operations/remove_user.ts @@ -2,14 +2,14 @@ import type { Db } from '../db'; import type { Server } from '../sdam/server'; import type { ClientSession } from '../sessions'; import type { Callback } from '../utils'; -import { CommandOperation, type CommandOperationOptions } from './command'; +import { CommandCallbackOperation, type CommandOperationOptions } from './command'; import { Aspect, defineAspects } from './operation'; /** @public */ export type RemoveUserOptions = CommandOperationOptions; /** @internal */ -export class RemoveUserOperation extends CommandOperation { +export class RemoveUserOperation extends CommandCallbackOperation { override options: RemoveUserOptions; username: string; @@ -24,7 +24,7 @@ export class RemoveUserOperation extends CommandOperation { session: ClientSession | undefined, callback: Callback ): void { - super.executeCommand(server, session, { dropUser: this.username }, err => { + super.executeCommandCallback(server, session, { dropUser: this.username }, err => { callback(err, err ? false : true); }); } diff --git a/src/operations/run_command.ts b/src/operations/run_command.ts index c11013a370..352965e836 100644 --- a/src/operations/run_command.ts +++ b/src/operations/run_command.ts @@ -3,7 +3,7 @@ import type { ReadPreferenceLike } from '../read_preference'; import type { Server } from '../sdam/server'; import type { ClientSession } from '../sessions'; import { type Callback, MongoDBNamespace } from '../utils'; -import { CommandOperation, type OperationParent } from './command'; +import { CommandCallbackOperation, type OperationParent } from './command'; /** @public */ export type RunCommandOptions = { @@ -45,7 +45,7 @@ export type RunCommandOptions = { } & BSONSerializeOptions; /** @internal */ -export class RunCommandOperation extends CommandOperation { +export class RunCommandOperation extends CommandCallbackOperation { override options: RunCommandOptions; command: Document; @@ -61,7 +61,7 @@ export class RunCommandOperation extends CommandOperation { callback: Callback ): void { const command = this.command; - this.executeCommand(server, session, command, callback); + this.executeCommandCallback(server, session, command, callback); } } diff --git a/src/operations/set_profiling_level.ts b/src/operations/set_profiling_level.ts index 2d097d2d25..d4416052d8 100644 --- a/src/operations/set_profiling_level.ts +++ b/src/operations/set_profiling_level.ts @@ -4,7 +4,7 @@ import type { Server } from '../sdam/server'; import type { ClientSession } from '../sessions'; import type { Callback } from '../utils'; import { enumToString } from '../utils'; -import { CommandOperation, type CommandOperationOptions } from './command'; +import { CommandCallbackOperation, type CommandOperationOptions } from './command'; const levelValues = new Set(['off', 'slow_only', 'all']); @@ -22,7 +22,7 @@ export type ProfilingLevel = (typeof ProfilingLevel)[keyof typeof ProfilingLevel export type SetProfilingLevelOptions = CommandOperationOptions; /** @internal */ -export class SetProfilingLevelOperation extends CommandOperation { +export class SetProfilingLevelOperation extends CommandCallbackOperation { override options: SetProfilingLevelOptions; level: ProfilingLevel; profile: 0 | 1 | 2; @@ -64,7 +64,7 @@ export class SetProfilingLevelOperation extends CommandOperation } // TODO(NODE-3483): Determine error to put here - super.executeCommand(server, session, { profile: this.profile }, (err, doc) => { + super.executeCommandCallback(server, session, { profile: this.profile }, (err, doc) => { if (err == null && doc.ok === 1) return callback(undefined, level); return err != null ? callback(err) diff --git a/src/operations/stats.ts b/src/operations/stats.ts index 5ecdea821c..c06d11cd9e 100644 --- a/src/operations/stats.ts +++ b/src/operations/stats.ts @@ -4,7 +4,7 @@ import type { Db } from '../db'; import type { Server } from '../sdam/server'; import type { ClientSession } from '../sessions'; import type { Callback } from '../utils'; -import { CommandOperation, type CommandOperationOptions } from './command'; +import { CommandCallbackOperation, type CommandOperationOptions } from './command'; import { Aspect, defineAspects } from './operation'; /** @@ -21,7 +21,7 @@ export interface CollStatsOptions extends CommandOperationOptions { * Get all the collection statistics. * @internal */ -export class CollStatsOperation extends CommandOperation { +export class CollStatsOperation extends CommandCallbackOperation { override options: CollStatsOptions; collectionName: string; @@ -47,7 +47,7 @@ export class CollStatsOperation extends CommandOperation { command.scale = this.options.scale; } - super.executeCommand(server, session, command, callback); + super.executeCommandCallback(server, session, command, callback); } } @@ -58,7 +58,7 @@ export interface DbStatsOptions extends CommandOperationOptions { } /** @internal */ -export class DbStatsOperation extends CommandOperation { +export class DbStatsOperation extends CommandCallbackOperation { override options: DbStatsOptions; constructor(db: Db, options: DbStatsOptions) { @@ -76,7 +76,7 @@ export class DbStatsOperation extends CommandOperation { command.scale = this.options.scale; } - super.executeCommand(server, session, command, callback); + super.executeCommandCallback(server, session, command, callback); } } diff --git a/src/operations/update.ts b/src/operations/update.ts index 2312353044..7a25a42984 100644 --- a/src/operations/update.ts +++ b/src/operations/update.ts @@ -5,7 +5,11 @@ import type { InferIdType } from '../mongo_types'; import type { Server } from '../sdam/server'; import type { ClientSession } from '../sessions'; import { type Callback, hasAtomicOperators, type MongoDBNamespace } from '../utils'; -import { type CollationOptions, CommandOperation, type CommandOperationOptions } from './command'; +import { + type CollationOptions, + CommandCallbackOperation, + type CommandOperationOptions +} from './command'; import { Aspect, defineAspects, type Hint } from './operation'; /** @public */ @@ -60,7 +64,7 @@ export interface UpdateStatement { } /** @internal */ -export class UpdateOperation extends CommandOperation { +export class UpdateOperation extends CommandCallbackOperation { override options: UpdateOptions & { ordered?: boolean }; statements: UpdateStatement[]; @@ -120,7 +124,7 @@ export class UpdateOperation extends CommandOperation { } } - super.executeCommand(server, session, command, callback); + super.executeCommandCallback(server, session, command, callback); } } diff --git a/src/operations/validate_collection.ts b/src/operations/validate_collection.ts index c47a0b81b5..3b48bbdce2 100644 --- a/src/operations/validate_collection.ts +++ b/src/operations/validate_collection.ts @@ -4,7 +4,7 @@ import { MongoRuntimeError } from '../error'; import type { Server } from '../sdam/server'; import type { ClientSession } from '../sessions'; import type { Callback } from '../utils'; -import { CommandOperation, type CommandOperationOptions } from './command'; +import { CommandCallbackOperation, type CommandOperationOptions } from './command'; /** @public */ export interface ValidateCollectionOptions extends CommandOperationOptions { @@ -13,7 +13,7 @@ export interface ValidateCollectionOptions extends CommandOperationOptions { } /** @internal */ -export class ValidateCollectionOperation extends CommandOperation { +export class ValidateCollectionOperation extends CommandCallbackOperation { override options: ValidateCollectionOptions; collectionName: string; command: Document; @@ -41,7 +41,7 @@ export class ValidateCollectionOperation extends CommandOperation { ): void { const collectionName = this.collectionName; - super.executeCommand(server, session, this.command, (err, doc) => { + super.executeCommandCallback(server, session, this.command, (err, doc) => { if (err != null) return callback(err); // TODO(NODE-3483): Replace these with MongoUnexpectedServerResponseError diff --git a/src/sdam/server.ts b/src/sdam/server.ts index 43f9d1ab6e..f76f8c9a8c 100644 --- a/src/sdam/server.ts +++ b/src/sdam/server.ts @@ -118,11 +118,7 @@ export class Server extends TypedEventEmitter { pool: ConnectionPool; serverApi?: ServerApi; hello?: Document; - commandAsync: ( - ns: MongoDBNamespace, - cmd: Document, - options: CommandOptions - ) => Promise; + commandAsync: (ns: MongoDBNamespace, cmd: Document, options: CommandOptions) => Promise; [kMonitor]: Monitor | null; /** @event */ @@ -151,7 +147,8 @@ export class Server extends TypedEventEmitter { ns: MongoDBNamespace, cmd: Document, options: CommandOptions, - callback: Callback + // callback type defines Document result because result is never nullish when it succeeds, otherwise promise rejects + callback: (error: Error | undefined, result: Document) => void ) => this.command(ns, cmd, options, callback as any) ); diff --git a/test/unit/operations/abstract_command.test.ts b/test/unit/operations/abstract_command.test.ts new file mode 100644 index 0000000000..c024db08a9 --- /dev/null +++ b/test/unit/operations/abstract_command.test.ts @@ -0,0 +1,57 @@ +import { expect } from 'chai'; +import * as sinon from 'sinon'; + +import { + type Callback, + type ClientSession, + CommandCallbackOperation, + CommandOperation, + type CommandOperationOptions, + type OperationParent, + Server, + ServerDescription +} from '../../mongodb'; +import { topologyWithPlaceholderClient } from '../../tools/utils'; + +class ConcreteCommand extends CommandCallbackOperation { + constructor(parent?: OperationParent, options?: CommandOperationOptions) { + super(parent, options); + } + + protected executeCallback( + server: Server, + session: ClientSession | undefined, + callback: Callback + ) { + super.execute(server, session).then( + res => callback(undefined, res), + err => callback(err, undefined) + ); + } +} + +describe('class CommandOperation', () => { + let server: Server; + beforeEach(() => { + server = new Server( + topologyWithPlaceholderClient([], {} as any), + new ServerDescription('a:1'), + {} as any + ); + }); + + context('when an operation uses CommandCallbackOperation', () => { + it('calls executeCommand when executeCommandCallback is invoked', done => { + const operation = new ConcreteCommand(); + const operationSpy = sinon.spy(CommandOperation.prototype, 'executeCommand'); + operation.executeCommandCallback(server, undefined, { ping: 1 }, () => { + try { + expect(operationSpy).to.have.been.calledOnceWithExactly(server, undefined, { ping: 1 }); + done(); + } catch (error) { + done(error); + } + }); + }); + }); +});