1
+ import { type Connection } from '..' ;
1
2
import type { BSONSerializeOptions , Document } from '../bson' ;
2
3
import { type MongoDBResponseConstructor } from '../cmap/wire_protocol/responses' ;
3
4
import { MongoInvalidArgumentError } from '../error' ;
@@ -9,14 +10,14 @@ import {
9
10
} from '../explain' ;
10
11
import { ReadConcern } from '../read_concern' ;
11
12
import type { ReadPreference } from '../read_preference' ;
12
- import type { Server } from '../sdam/server' ;
13
+ import type { Server , ServerCommandOptions } from '../sdam/server' ;
13
14
import { MIN_SECONDARY_WRITE_WIRE_VERSION } from '../sdam/server_selection' ;
14
15
import type { ClientSession } from '../sessions' ;
15
16
import { type TimeoutContext } from '../timeout' ;
16
17
import { commandSupportsReadConcern , maxWireVersion , MongoDBNamespace } from '../utils' ;
17
18
import { WriteConcern , type WriteConcernOptions } from '../write_concern' ;
18
19
import type { ReadConcernLike } from './../read_concern' ;
19
- import { AbstractOperation , Aspect , type OperationOptions } from './operation' ;
20
+ import { AbstractOperation , Aspect , ModernizedOperation , type OperationOptions } from './operation' ;
20
21
21
22
/** @public */
22
23
export interface CollationOptions {
@@ -183,3 +184,94 @@ export abstract class CommandOperation<T> extends AbstractOperation<T> {
183
184
return await server . command ( this . ns , cmd , options , responseType ) ;
184
185
}
185
186
}
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