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,94 @@ export abstract class CommandOperation<T> extends AbstractOperation<T> {
183184 return await server . command ( this . ns , cmd , options , responseType ) ;
184185 }
185186}
187+
188+ /** @internal */
189+ export abstract class ModernizedCommandOperation < T > extends ModernizedOperation < T > {
190+ override options : CommandOperationOptions ;
191+ readConcern ?: ReadConcern ;
192+ writeConcern ?: WriteConcern ;
193+ explain ?: Explain ;
194+
195+ constructor ( parent ?: OperationParent , options ?: CommandOperationOptions ) {
196+ super ( options ) ;
197+ this . options = options ?? { } ;
198+
199+ // NOTE: this was explicitly added for the add/remove user operations, it's likely
200+ // something we'd want to reconsider. Perhaps those commands can use `Admin`
201+ // as a parent?
202+ const dbNameOverride = options ?. dbName || options ?. authdb ;
203+ if ( dbNameOverride ) {
204+ this . ns = new MongoDBNamespace ( dbNameOverride , '$cmd' ) ;
205+ } else {
206+ this . ns = parent
207+ ? parent . s . namespace . withCollection ( '$cmd' )
208+ : new MongoDBNamespace ( 'admin' , '$cmd' ) ;
209+ }
210+
211+ this . readConcern = ReadConcern . fromOptions ( options ) ;
212+ this . writeConcern = WriteConcern . fromOptions ( options ) ;
213+
214+ if ( this . hasAspect ( Aspect . EXPLAINABLE ) ) {
215+ this . explain = Explain . fromOptions ( options ) ;
216+ if ( this . explain ) validateExplainTimeoutOptions ( this . options , this . explain ) ;
217+ } else if ( options ?. explain != null ) {
218+ throw new MongoInvalidArgumentError ( `Option "explain" is not supported on this command` ) ;
219+ }
220+ }
221+
222+ override get canRetryWrite ( ) : boolean {
223+ if ( this . hasAspect ( Aspect . EXPLAINABLE ) ) {
224+ return this . explain == null ;
225+ }
226+ return super . canRetryWrite ;
227+ }
228+
229+ abstract buildCommandDocument ( connection : Connection , session ?: ClientSession ) : Document ;
230+
231+ override buildOptions ( timeoutContext : TimeoutContext ) : ServerCommandOptions {
232+ return {
233+ ...this . options ,
234+ ...this . bsonOptions ,
235+ timeoutContext,
236+ readPreference : this . readPreference ,
237+ session : this . session
238+ } ;
239+ }
240+
241+ override buildCommand ( connection : Connection , session ?: ClientSession ) : Document {
242+ const command = this . buildCommandDocument ( connection , session ) ;
243+
244+ const serverWireVersion = maxWireVersion ( connection ) ;
245+ const inTransaction = this . session && this . session . inTransaction ( ) ;
246+
247+ if ( this . readConcern && commandSupportsReadConcern ( command ) && ! inTransaction ) {
248+ Object . assign ( command , { readConcern : this . readConcern } ) ;
249+ }
250+
251+ if ( this . trySecondaryWrite && serverWireVersion < MIN_SECONDARY_WRITE_WIRE_VERSION ) {
252+ command . omitReadPreference = true ;
253+ }
254+
255+ if ( this . writeConcern && this . hasAspect ( Aspect . WRITE_OPERATION ) && ! inTransaction ) {
256+ WriteConcern . apply ( command , this . writeConcern ) ;
257+ }
258+
259+ if (
260+ this . options . collation &&
261+ typeof this . options . collation === 'object' &&
262+ ! this . hasAspect ( Aspect . SKIP_COLLATION )
263+ ) {
264+ Object . assign ( command , { collation : this . options . collation } ) ;
265+ }
266+
267+ if ( typeof this . options . maxTimeMS === 'number' ) {
268+ command . maxTimeMS = this . options . maxTimeMS ;
269+ }
270+
271+ if ( this . hasAspect ( Aspect . EXPLAINABLE ) && this . explain ) {
272+ return decorateWithExplain ( command , this . explain ) ;
273+ }
274+
275+ return command ;
276+ }
277+ }
0 commit comments