Skip to content

Commit 410510a

Browse files
authored
Merge branch 'main' into NODE-5409-socks-optional
2 parents 1c6fdcb + 52cd649 commit 410510a

File tree

9 files changed

+95
-118
lines changed

9 files changed

+95
-118
lines changed

Diff for: src/admin.ts

+7-3
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import type { Document } from './bson';
1+
import { type Document, resolveBSONOptions } from './bson';
22
import type { Db } from './db';
33
import { AddUserOperation, type AddUserOptions } from './operations/add_user';
44
import type { CommandOperationOptions } from './operations/command';
@@ -9,7 +9,7 @@ import {
99
type ListDatabasesResult
1010
} from './operations/list_databases';
1111
import { RemoveUserOperation, type RemoveUserOptions } from './operations/remove_user';
12-
import { RunCommandOperation, type RunCommandOptions } from './operations/run_command';
12+
import { RunAdminCommandOperation, type RunCommandOptions } from './operations/run_command';
1313
import {
1414
ValidateCollectionOperation,
1515
type ValidateCollectionOptions
@@ -76,7 +76,11 @@ export class Admin {
7676
async command(command: Document, options?: RunCommandOptions): Promise<Document> {
7777
return executeOperation(
7878
this.s.db.client,
79-
new RunCommandOperation(this.s.db, command, { dbName: 'admin', ...options })
79+
new RunAdminCommandOperation(command, {
80+
...resolveBSONOptions(options),
81+
session: options?.session,
82+
readPreference: options?.readPreference
83+
})
8084
);
8185
}
8286

Diff for: src/db.ts

+8-1
Original file line numberDiff line numberDiff line change
@@ -259,7 +259,14 @@ export class Db {
259259
*/
260260
async command(command: Document, options?: RunCommandOptions): Promise<Document> {
261261
// Intentionally, we do not inherit options from parent for this operation.
262-
return executeOperation(this.client, new RunCommandOperation(this, command, options));
262+
return executeOperation(
263+
this.client,
264+
new RunCommandOperation(this, command, {
265+
...resolveBSONOptions(options),
266+
session: options?.session,
267+
readPreference: options?.readPreference
268+
})
269+
);
263270
}
264271

265272
/**

Diff for: src/mongo_client.ts

+6-3
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,8 @@ import type { Encrypter } from './encrypter';
2323
import { MongoInvalidArgumentError } from './error';
2424
import { MongoLogger, type MongoLoggerOptions } from './mongo_logger';
2525
import { TypedEventEmitter } from './mongo_types';
26+
import { executeOperation } from './operations/execute_operation';
27+
import { RunAdminCommandOperation } from './operations/run_command';
2628
import type { ReadConcern, ReadConcernLevel, ReadConcernLike } from './read_concern';
2729
import { ReadPreference, type ReadPreferenceMode } from './read_preference';
2830
import type { TagSet } from './sdam/server_description';
@@ -521,12 +523,13 @@ export class MongoClient extends TypedEventEmitter<MongoClientEvents> {
521523
if (servers.length !== 0) {
522524
const endSessions = Array.from(this.s.sessionPool.sessions, ({ id }) => id);
523525
if (endSessions.length !== 0) {
524-
await this.db('admin')
525-
.command(
526+
await executeOperation(
527+
this,
528+
new RunAdminCommandOperation(
526529
{ endSessions },
527530
{ readPreference: ReadPreference.primaryPreferred, noResponse: true }
528531
)
529-
.catch(() => null); // outcome does not matter
532+
).catch(() => null); // outcome does not matter;
530533
}
531534
}
532535

Diff for: src/operations/operation.ts

-1
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,6 @@ const kSession = Symbol('session');
4949
*/
5050
export abstract class AbstractOperation<TResult = any> {
5151
ns!: MongoDBNamespace;
52-
cmd!: Document;
5352
readPreference: ReadPreference;
5453
server!: Server;
5554
bypassPinningCheck: boolean;

Diff for: src/operations/rename.ts

+27-32
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,10 @@
11
import type { Document } from '../bson';
22
import { Collection } from '../collection';
3-
import { MongoServerError } from '../error';
43
import type { Server } from '../sdam/server';
54
import type { ClientSession } from '../sessions';
6-
import { checkCollectionName } from '../utils';
7-
import type { CommandOperationOptions } from './command';
5+
import { checkCollectionName, MongoDBNamespace } from '../utils';
6+
import { CommandOperation, type CommandOperationOptions } from './command';
87
import { Aspect, defineAspects } from './operation';
9-
import { RunAdminCommandOperation } from './run_command';
108

119
/** @public */
1210
export interface RenameOptions extends CommandOperationOptions {
@@ -17,39 +15,36 @@ export interface RenameOptions extends CommandOperationOptions {
1715
}
1816

1917
/** @internal */
20-
export class RenameOperation extends RunAdminCommandOperation {
21-
override options: RenameOptions;
22-
collection: Collection;
23-
newName: string;
24-
25-
constructor(collection: Collection, newName: string, options: RenameOptions) {
26-
// Check the collection name
18+
export class RenameOperation extends CommandOperation<Document> {
19+
constructor(
20+
public collection: Collection,
21+
public newName: string,
22+
public override options: RenameOptions
23+
) {
2724
checkCollectionName(newName);
28-
29-
// Build the command
30-
const renameCollection = collection.namespace;
31-
const toCollection = collection.s.namespace.withCollection(newName).toString();
32-
const dropTarget = typeof options.dropTarget === 'boolean' ? options.dropTarget : false;
33-
const cmd = { renameCollection: renameCollection, to: toCollection, dropTarget: dropTarget };
34-
35-
super(collection, cmd, options);
36-
this.options = options;
37-
this.collection = collection;
38-
this.newName = newName;
25+
super(collection, options);
26+
this.ns = new MongoDBNamespace('admin', '$cmd');
3927
}
4028

4129
override async execute(server: Server, session: ClientSession | undefined): Promise<Collection> {
42-
const coll = this.collection;
43-
44-
const doc = await super.execute(server, session);
45-
// We have an error
46-
if (doc?.errmsg) {
47-
throw new MongoServerError(doc);
48-
}
49-
50-
const newColl: Collection<Document> = new Collection(coll.s.db, this.newName, coll.s.options);
30+
// Build the command
31+
const renameCollection = this.collection.namespace;
32+
const toCollection = this.collection.s.namespace.withCollection(this.newName).toString();
33+
const dropTarget =
34+
typeof this.options.dropTarget === 'boolean' ? this.options.dropTarget : false;
35+
36+
const command = {
37+
renameCollection: renameCollection,
38+
to: toCollection,
39+
dropTarget: dropTarget
40+
};
41+
42+
await super.executeCommand(server, session, command);
43+
return new Collection(this.collection.s.db, this.newName, this.collection.s.options);
44+
}
5145

52-
return newColl;
46+
protected executeCallback(_server: any, _session: any, _callback: any): void {
47+
throw new Error('Method not implemented.');
5348
}
5449
}
5550

Diff for: src/operations/run_command.ts

+34-52
Original file line numberDiff line numberDiff line change
@@ -1,73 +1,55 @@
11
import type { BSONSerializeOptions, Document } from '../bson';
2+
import { type Db } from '../db';
3+
import { type TODO_NODE_3286 } from '../mongo_types';
24
import type { ReadPreferenceLike } from '../read_preference';
35
import type { Server } from '../sdam/server';
46
import type { ClientSession } from '../sessions';
5-
import { type Callback, MongoDBNamespace } from '../utils';
6-
import { CommandCallbackOperation, type OperationParent } from './command';
7+
import { MongoDBNamespace } from '../utils';
8+
import { AbstractOperation } from './operation';
79

810
/** @public */
911
export type RunCommandOptions = {
1012
/** Specify ClientSession for this command */
1113
session?: ClientSession;
1214
/** The read preference */
1315
readPreference?: ReadPreferenceLike;
14-
15-
// The following options were "accidentally" supported
16-
// Since the options are generally supported through inheritance
17-
18-
/** @deprecated This is an internal option that has undefined behavior for this API */
19-
willRetryWrite?: any;
20-
/** @deprecated This is an internal option that has undefined behavior for this API */
21-
omitReadPreference?: any;
22-
/** @deprecated This is an internal option that has undefined behavior for this API */
23-
writeConcern?: any;
24-
/** @deprecated This is an internal option that has undefined behavior for this API */
25-
explain?: any;
26-
/** @deprecated This is an internal option that has undefined behavior for this API */
27-
readConcern?: any;
28-
/** @deprecated This is an internal option that has undefined behavior for this API */
29-
collation?: any;
30-
/** @deprecated This is an internal option that has undefined behavior for this API */
31-
maxTimeMS?: any;
32-
/** @deprecated This is an internal option that has undefined behavior for this API */
33-
comment?: any;
34-
/** @deprecated This is an internal option that has undefined behavior for this API */
35-
retryWrites?: any;
36-
/** @deprecated This is an internal option that has undefined behavior for this API */
37-
dbName?: any;
38-
/** @deprecated This is an internal option that has undefined behavior for this API */
39-
authdb?: any;
40-
/** @deprecated This is an internal option that has undefined behavior for this API */
41-
noResponse?: any;
42-
43-
/** @internal Used for transaction commands */
44-
bypassPinningCheck?: boolean;
4516
} & BSONSerializeOptions;
4617

4718
/** @internal */
48-
export class RunCommandOperation<T = Document> extends CommandCallbackOperation<T> {
49-
override options: RunCommandOptions;
50-
command: Document;
51-
52-
constructor(parent: OperationParent | undefined, command: Document, options?: RunCommandOptions) {
53-
super(parent, options);
54-
this.options = options ?? {};
55-
this.command = command;
19+
export class RunCommandOperation<T = Document> extends AbstractOperation<T> {
20+
constructor(parent: Db, public command: Document, public override options: RunCommandOptions) {
21+
super(options);
22+
this.ns = parent.s.namespace.withCollection('$cmd');
5623
}
5724

58-
override executeCallback(
59-
server: Server,
60-
session: ClientSession | undefined,
61-
callback: Callback<T>
62-
): void {
63-
const command = this.command;
64-
this.executeCommandCallback(server, session, command, callback);
25+
override async execute(server: Server, session: ClientSession | undefined): Promise<T> {
26+
this.server = server;
27+
return server.commandAsync(this.ns, this.command, {
28+
...this.options,
29+
readPreference: this.readPreference,
30+
session
31+
}) as TODO_NODE_3286;
6532
}
6633
}
6734

68-
export class RunAdminCommandOperation<T = Document> extends RunCommandOperation<T> {
69-
constructor(parent: OperationParent | undefined, command: Document, options?: RunCommandOptions) {
70-
super(parent, command, options);
71-
this.ns = new MongoDBNamespace('admin');
35+
export class RunAdminCommandOperation<T = Document> extends AbstractOperation<T> {
36+
constructor(
37+
public command: Document,
38+
public override options: RunCommandOptions & {
39+
noResponse?: boolean;
40+
bypassPinningCheck?: boolean;
41+
}
42+
) {
43+
super(options);
44+
this.ns = new MongoDBNamespace('admin', '$cmd');
45+
}
46+
47+
override async execute(server: Server, session: ClientSession | undefined): Promise<T> {
48+
this.server = server;
49+
return server.commandAsync(this.ns, this.command, {
50+
...this.options,
51+
readPreference: this.readPreference,
52+
session
53+
}) as TODO_NODE_3286;
7254
}
7355
}

Diff for: src/sessions.ts

+3-3
Original file line numberDiff line numberDiff line change
@@ -754,7 +754,7 @@ function endTransaction(
754754
// send the command
755755
executeOperation(
756756
session.client,
757-
new RunAdminCommandOperation(undefined, command, {
757+
new RunAdminCommandOperation(command, {
758758
session,
759759
readPreference: ReadPreference.primary,
760760
bypassPinningCheck: true
@@ -778,7 +778,7 @@ function endTransaction(
778778

779779
return executeOperation(
780780
session.client,
781-
new RunAdminCommandOperation(undefined, command, {
781+
new RunAdminCommandOperation(command, {
782782
session,
783783
readPreference: ReadPreference.primary,
784784
bypassPinningCheck: true
@@ -989,7 +989,7 @@ export function applySession(
989989
if (
990990
session.supports.causalConsistency &&
991991
session.operationTime &&
992-
commandSupportsReadConcern(command, options)
992+
commandSupportsReadConcern(command)
993993
) {
994994
command.readConcern = command.readConcern || {};
995995
Object.assign(command.readConcern, { afterClusterTime: session.operationTime });

Diff for: src/utils.ts

+1-10
Original file line numberDiff line numberDiff line change
@@ -1155,20 +1155,11 @@ export function shuffle<T>(sequence: Iterable<T>, limit = 0): Array<T> {
11551155

11561156
// TODO(NODE-4936): read concern eligibility for commands should be codified in command construction
11571157
// @see https://github.com/mongodb/specifications/blob/master/source/read-write-concern/read-write-concern.rst#read-concern
1158-
export function commandSupportsReadConcern(command: Document, options?: Document): boolean {
1158+
export function commandSupportsReadConcern(command: Document): boolean {
11591159
if (command.aggregate || command.count || command.distinct || command.find || command.geoNear) {
11601160
return true;
11611161
}
11621162

1163-
if (
1164-
command.mapReduce &&
1165-
options &&
1166-
options.out &&
1167-
(options.out.inline === 1 || options.out === 'inline')
1168-
) {
1169-
return true;
1170-
}
1171-
11721163
return false;
11731164
}
11741165

Diff for: test/integration/run-command/run_command.test.ts

+9-13
Original file line numberDiff line numberDiff line change
@@ -37,22 +37,18 @@ describe('RunCommand API', () => {
3737

3838
it('does not support writeConcern in options', { requires: { mongodb: '>=5.0' } }, async () => {
3939
const command = Object.freeze({ insert: 'test', documents: [{ x: 1 }] });
40+
//@ts-expect-error: Testing WC is not supported
4041
await db.command(command, { writeConcern: new WriteConcern('majority') });
4142
expect(commandsStarted).to.not.have.nested.property('[0].command.writeConcern');
4243
expect(command).to.not.have.property('writeConcern');
4344
});
4445

45-
// TODO(NODE-4936): We do support readConcern in options, the spec forbids this
46-
it.skip(
47-
'does not support readConcern in options',
48-
{ requires: { mongodb: '>=5.0' } },
49-
async () => {
50-
const command = Object.freeze({ find: 'test', filter: {} });
51-
const res = await db.command(command, { readConcern: ReadConcern.MAJORITY });
52-
expect(res).to.have.property('ok', 1);
53-
expect(commandsStarted).to.not.have.nested.property('[0].command.readConcern');
54-
expect(command).to.not.have.property('readConcern');
55-
}
56-
).skipReason =
57-
'TODO(NODE-4936): Enable this test when readConcern support has been removed from runCommand';
46+
it('does not support readConcern in options', { requires: { mongodb: '>=5.0' } }, async () => {
47+
const command = Object.freeze({ find: 'test', filter: {} });
48+
//@ts-expect-error: Testing RC is not supported
49+
const res = await db.command(command, { readConcern: ReadConcern.MAJORITY });
50+
expect(res).to.have.property('ok', 1);
51+
expect(commandsStarted).to.not.have.nested.property('[0].command.readConcern');
52+
expect(command).to.not.have.property('readConcern');
53+
});
5854
});

0 commit comments

Comments
 (0)