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,93 @@ 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
+ 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