1+ import { type Connection } from '..' ;
12import type { BSONSerializeOptions , Document } from '../bson' ;
23import { type MongoDBResponseConstructor } from '../cmap/wire_protocol/responses' ;
34import { MongoInvalidArgumentError } from '../error' ;
@@ -9,14 +10,14 @@ import {
910} from '../explain' ;
1011import { ReadConcern } from '../read_concern' ;
1112import type { ReadPreference } from '../read_preference' ;
12- import type { Server } from '../sdam/server' ;
13+ import type { Server , ServerCommandOptions } from '../sdam/server' ;
1314import { MIN_SECONDARY_WRITE_WIRE_VERSION } from '../sdam/server_selection' ;
1415import type { ClientSession } from '../sessions' ;
1516import { type TimeoutContext } from '../timeout' ;
1617import { commandSupportsReadConcern , maxWireVersion , MongoDBNamespace } from '../utils' ;
1718import { WriteConcern , type WriteConcernOptions } from '../write_concern' ;
1819import type { ReadConcernLike } from './../read_concern' ;
19- import { AbstractOperation , Aspect , type OperationOptions } from './operation' ;
20+ import { AbstractOperation , Aspect , ModernizedOperation , type OperationOptions } from './operation' ;
2021
2122/** @public */
2223export interface CollationOptions {
@@ -183,3 +184,93 @@ export abstract class CommandOperation<T> extends AbstractOperation<T> {
183184 return await server . command ( this . ns , cmd , options , responseType ) ;
184185 }
185186}
187+
188+ export abstract class ModernizedCommandOperation < T > extends ModernizedOperation < T > {
189+ override options : CommandOperationOptions ;
190+ readConcern ?: ReadConcern ;
191+ writeConcern ?: WriteConcern ;
192+ explain ?: Explain ;
193+
194+ constructor ( parent ?: OperationParent , options ?: CommandOperationOptions ) {
195+ super ( options ) ;
196+ this . options = options ?? { } ;
197+
198+ // NOTE: this was explicitly added for the add/remove user operations, it's likely
199+ // something we'd want to reconsider. Perhaps those commands can use `Admin`
200+ // as a parent?
201+ const dbNameOverride = options ?. dbName || options ?. authdb ;
202+ if ( dbNameOverride ) {
203+ this . ns = new MongoDBNamespace ( dbNameOverride , '$cmd' ) ;
204+ } else {
205+ this . ns = parent
206+ ? parent . s . namespace . withCollection ( '$cmd' )
207+ : new MongoDBNamespace ( 'admin' , '$cmd' ) ;
208+ }
209+
210+ this . readConcern = ReadConcern . fromOptions ( options ) ;
211+ this . writeConcern = WriteConcern . fromOptions ( options ) ;
212+
213+ if ( this . hasAspect ( Aspect . EXPLAINABLE ) ) {
214+ this . explain = Explain . fromOptions ( options ) ;
215+ if ( this . explain ) validateExplainTimeoutOptions ( this . options , this . explain ) ;
216+ } else if ( options ?. explain != null ) {
217+ throw new MongoInvalidArgumentError ( `Option "explain" is not supported on this command` ) ;
218+ }
219+ }
220+
221+ override get canRetryWrite ( ) : boolean {
222+ if ( this . hasAspect ( Aspect . EXPLAINABLE ) ) {
223+ return this . explain == null ;
224+ }
225+ return super . canRetryWrite ;
226+ }
227+
228+ abstract buildCommandDocument ( connection : Connection , session ?: ClientSession ) : Document ;
229+
230+ override buildOptions ( timeoutContext : TimeoutContext ) : ServerCommandOptions {
231+ return {
232+ ...this . options ,
233+ ...this . bsonOptions ,
234+ timeoutContext,
235+ readPreference : this . readPreference ,
236+ session : this . session
237+ } ;
238+ }
239+
240+ override buildCommand ( connection : Connection , session ?: ClientSession ) : Document {
241+ const command = this . buildCommandDocument ( connection , session ) ;
242+
243+ const serverWireVersion = maxWireVersion ( connection ) ;
244+ const inTransaction = this . session && this . session . inTransaction ( ) ;
245+
246+ if ( this . readConcern && commandSupportsReadConcern ( command ) && ! inTransaction ) {
247+ Object . assign ( command , { readConcern : this . readConcern } ) ;
248+ }
249+
250+ if ( this . trySecondaryWrite && serverWireVersion < MIN_SECONDARY_WRITE_WIRE_VERSION ) {
251+ command . omitReadPreference = true ;
252+ }
253+
254+ if ( this . writeConcern && this . hasAspect ( Aspect . WRITE_OPERATION ) && ! inTransaction ) {
255+ WriteConcern . apply ( command , this . writeConcern ) ;
256+ }
257+
258+ if (
259+ this . options . collation &&
260+ typeof this . options . collation === 'object' &&
261+ ! this . hasAspect ( Aspect . SKIP_COLLATION )
262+ ) {
263+ Object . assign ( command , { collation : this . options . collation } ) ;
264+ }
265+
266+ if ( typeof this . options . maxTimeMS === 'number' ) {
267+ command . maxTimeMS = this . options . maxTimeMS ;
268+ }
269+
270+ if ( this . hasAspect ( Aspect . EXPLAINABLE ) && this . explain ) {
271+ return decorateWithExplain ( command , this . explain ) ;
272+ }
273+
274+ return command ;
275+ }
276+ }
0 commit comments