Skip to content

Commit f697ee1

Browse files
authored
refactor(NODE-5392): refactor cursor operations to use async/await (mongodb#3767)
1 parent 7ade907 commit f697ee1

File tree

12 files changed

+160
-317
lines changed

12 files changed

+160
-317
lines changed

src/operations/collections.ts

Lines changed: 14 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -2,15 +2,14 @@ import { Collection } from '../collection';
22
import type { Db } from '../db';
33
import type { Server } from '../sdam/server';
44
import type { ClientSession } from '../sessions';
5-
import type { Callback } from '../utils';
6-
import { AbstractCallbackOperation, type OperationOptions } from './operation';
5+
import { AbstractOperation, type OperationOptions } from './operation';
76

87
export interface CollectionsOptions extends OperationOptions {
98
nameOnly?: boolean;
109
}
1110

1211
/** @internal */
13-
export class CollectionsOperation extends AbstractCallbackOperation<Collection[]> {
12+
export class CollectionsOperation extends AbstractOperation<Collection[]> {
1413
override options: CollectionsOptions;
1514
db: Db;
1615

@@ -20,31 +19,22 @@ export class CollectionsOperation extends AbstractCallbackOperation<Collection[]
2019
this.db = db;
2120
}
2221

23-
override executeCallback(
24-
server: Server,
25-
session: ClientSession | undefined,
26-
callback: Callback<Collection[]>
27-
): void {
22+
async execute(server: Server, session: ClientSession | undefined): Promise<Collection[]> {
2823
// Let's get the collection names
29-
this.db
24+
const documents = await this.db
3025
.listCollections(
3126
{},
3227
{ ...this.options, nameOnly: true, readPreference: this.readPreference, session }
3328
)
34-
.toArray()
35-
.then(
36-
documents => {
37-
const collections = [];
38-
for (const { name } of documents) {
39-
if (!name.includes('$')) {
40-
// Filter collections removing any illegal ones
41-
collections.push(new Collection(this.db, name, this.db.s.options));
42-
}
43-
}
44-
// Return the collection objects
45-
callback(undefined, collections);
46-
},
47-
error => callback(error)
48-
);
29+
.toArray();
30+
const collections: Collection[] = [];
31+
for (const { name } of documents) {
32+
if (!name.includes('$')) {
33+
// Filter collections removing any illegal ones
34+
collections.push(new Collection(this.db, name, this.db.s.options));
35+
}
36+
}
37+
// Return the collection objects
38+
return collections;
4939
}
5040
}

src/operations/execute_operation.ts

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -25,13 +25,13 @@ import {
2525
import type { Topology } from '../sdam/topology';
2626
import type { ClientSession } from '../sessions';
2727
import { type Callback, maybeCallback, supportsRetryableWrites } from '../utils';
28-
import { AbstractCallbackOperation, Aspect } from './operation';
28+
import { AbstractOperation, Aspect } from './operation';
2929

3030
const MMAPv1_RETRY_WRITES_ERROR_CODE = MONGODB_ERROR_CODES.IllegalOperation;
3131
const MMAPv1_RETRY_WRITES_ERROR_MESSAGE =
3232
'This MongoDB deployment does not support retryable writes. Please add retryWrites=false to your connection string.';
3333

34-
type ResultTypeFromOperation<TOperation> = TOperation extends AbstractCallbackOperation<infer K>
34+
type ResultTypeFromOperation<TOperation> = TOperation extends AbstractOperation<infer K>
3535
? K
3636
: never;
3737

@@ -61,29 +61,29 @@ export interface ExecutionResult {
6161
* @param callback - The command result callback
6262
*/
6363
export function executeOperation<
64-
T extends AbstractCallbackOperation<TResult>,
64+
T extends AbstractOperation<TResult>,
6565
TResult = ResultTypeFromOperation<T>
6666
>(client: MongoClient, operation: T): Promise<TResult>;
6767
export function executeOperation<
68-
T extends AbstractCallbackOperation<TResult>,
68+
T extends AbstractOperation<TResult>,
6969
TResult = ResultTypeFromOperation<T>
7070
>(client: MongoClient, operation: T, callback: Callback<TResult>): void;
7171
export function executeOperation<
72-
T extends AbstractCallbackOperation<TResult>,
72+
T extends AbstractOperation<TResult>,
7373
TResult = ResultTypeFromOperation<T>
7474
>(client: MongoClient, operation: T, callback?: Callback<TResult>): Promise<TResult> | void;
7575
export function executeOperation<
76-
T extends AbstractCallbackOperation<TResult>,
76+
T extends AbstractOperation<TResult>,
7777
TResult = ResultTypeFromOperation<T>
7878
>(client: MongoClient, operation: T, callback?: Callback<TResult>): Promise<TResult> | void {
7979
return maybeCallback(() => executeOperationAsync(client, operation), callback);
8080
}
8181

8282
async function executeOperationAsync<
83-
T extends AbstractCallbackOperation<TResult>,
83+
T extends AbstractOperation<TResult>,
8484
TResult = ResultTypeFromOperation<T>
8585
>(client: MongoClient, operation: T): Promise<TResult> {
86-
if (!(operation instanceof AbstractCallbackOperation)) {
86+
if (!(operation instanceof AbstractOperation)) {
8787
// TODO(NODE-3483): Extend MongoRuntimeError
8888
throw new MongoRuntimeError('This method requires a valid operation instance');
8989
}
@@ -209,7 +209,7 @@ type RetryOptions = {
209209
};
210210

211211
async function retryOperation<
212-
T extends AbstractCallbackOperation<TResult>,
212+
T extends AbstractOperation<TResult>,
213213
TResult = ResultTypeFromOperation<T>
214214
>(
215215
operation: T,

src/operations/find.ts

Lines changed: 17 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -11,11 +11,7 @@ import {
1111
type MongoDBNamespace,
1212
normalizeHintField
1313
} from '../utils';
14-
import {
15-
type CollationOptions,
16-
CommandCallbackOperation,
17-
type CommandOperationOptions
18-
} from './command';
14+
import { type CollationOptions, CommandOperation, type CommandOperationOptions } from './command';
1915
import { Aspect, defineAspects, type Hint } from './operation';
2016

2117
/**
@@ -75,7 +71,7 @@ export interface FindOptions<TSchema extends Document = Document>
7571
}
7672

7773
/** @internal */
78-
export class FindOperation extends CommandCallbackOperation<Document> {
74+
export class FindOperation extends CommandOperation<Document> {
7975
/**
8076
* @remarks WriteConcern can still be present on the options because
8177
* we inherit options from the client/db/collection. The
@@ -106,11 +102,7 @@ export class FindOperation extends CommandCallbackOperation<Document> {
106102
this.filter = filter != null && filter._bsontype === 'ObjectId' ? { _id: filter } : filter;
107103
}
108104

109-
override executeCallback(
110-
server: Server,
111-
session: ClientSession | undefined,
112-
callback: Callback<Document>
113-
): void {
105+
override async execute(server: Server, session: ClientSession | undefined): Promise<Document> {
114106
this.server = server;
115107

116108
const options = this.options;
@@ -120,17 +112,20 @@ export class FindOperation extends CommandCallbackOperation<Document> {
120112
findCommand = decorateWithExplain(findCommand, this.explain);
121113
}
122114

123-
server.command(
124-
this.ns,
125-
findCommand,
126-
{
127-
...this.options,
128-
...this.bsonOptions,
129-
documentsReturnedIn: 'firstBatch',
130-
session
131-
},
132-
callback
133-
);
115+
return server.commandAsync(this.ns, findCommand, {
116+
...this.options,
117+
...this.bsonOptions,
118+
documentsReturnedIn: 'firstBatch',
119+
session
120+
});
121+
}
122+
123+
protected executeCallback(
124+
_server: Server,
125+
_session: ClientSession | undefined,
126+
_callback: Callback<Document>
127+
): void {
128+
throw new Error('Method not implemented.');
134129
}
135130
}
136131

src/operations/get_more.ts

Lines changed: 8 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,8 @@ import type { Document, Long } from '../bson';
22
import { MongoRuntimeError } from '../error';
33
import type { Server } from '../sdam/server';
44
import type { ClientSession } from '../sessions';
5-
import { type Callback, maxWireVersion, type MongoDBNamespace } from '../utils';
6-
import {
7-
AbstractCallbackOperation,
8-
Aspect,
9-
defineAspects,
10-
type OperationOptions
11-
} from './operation';
5+
import { maxWireVersion, type MongoDBNamespace } from '../utils';
6+
import { AbstractOperation, Aspect, defineAspects, type OperationOptions } from './operation';
127

138
/** @internal */
149
export interface GetMoreOptions extends OperationOptions {
@@ -40,7 +35,7 @@ export interface GetMoreCommand {
4035
}
4136

4237
/** @internal */
43-
export class GetMoreOperation extends AbstractCallbackOperation {
38+
export class GetMoreOperation extends AbstractOperation {
4439
cursorId: Long;
4540
override options: GetMoreOptions;
4641

@@ -57,26 +52,20 @@ export class GetMoreOperation extends AbstractCallbackOperation {
5752
* Although there is a server already associated with the get more operation, the signature
5853
* for execute passes a server so we will just use that one.
5954
*/
60-
override executeCallback(
61-
server: Server,
62-
session: ClientSession | undefined,
63-
callback: Callback<Document>
64-
): void {
55+
async execute(server: Server, _session: ClientSession | undefined): Promise<Document> {
6556
if (server !== this.server) {
66-
return callback(
67-
new MongoRuntimeError('Getmore must run on the same server operation began on')
68-
);
57+
throw new MongoRuntimeError('Getmore must run on the same server operation began on');
6958
}
7059

7160
if (this.cursorId == null || this.cursorId.isZero()) {
72-
return callback(new MongoRuntimeError('Unable to iterate cursor with no id'));
61+
throw new MongoRuntimeError('Unable to iterate cursor with no id');
7362
}
7463

7564
const collection = this.ns.collection;
7665
if (collection == null) {
7766
// Cursors should have adopted the namespace returned by MongoDB
7867
// which should always defined a collection name (even a pseudo one, ex. db.aggregate())
79-
return callback(new MongoRuntimeError('A collection name must be determined before getMore'));
68+
throw new MongoRuntimeError('A collection name must be determined before getMore');
8069
}
8170

8271
const getMoreCmd: GetMoreCommand = {
@@ -104,7 +93,7 @@ export class GetMoreOperation extends AbstractCallbackOperation {
10493
...this.options
10594
};
10695

107-
server.command(this.ns, getMoreCmd, commandOptions, callback);
96+
return server.commandAsync(this.ns, getMoreCmd, commandOptions);
10897
}
10998
}
11099

src/operations/indexes.ts

Lines changed: 12 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import { type Callback, isObject, maxWireVersion, type MongoDBNamespace } from '
1010
import {
1111
type CollationOptions,
1212
CommandCallbackOperation,
13+
CommandOperation,
1314
type CommandOperationOptions,
1415
type OperationParent
1516
} from './command';
@@ -396,7 +397,7 @@ export interface ListIndexesOptions extends Omit<CommandOperationOptions, 'write
396397
}
397398

398399
/** @internal */
399-
export class ListIndexesOperation extends CommandCallbackOperation<Document> {
400+
export class ListIndexesOperation extends CommandOperation<Document> {
400401
/**
401402
* @remarks WriteConcern can still be present on the options because
402403
* we inherit options from the client/db/collection. The
@@ -415,11 +416,7 @@ export class ListIndexesOperation extends CommandCallbackOperation<Document> {
415416
this.collectionNamespace = collection.s.namespace;
416417
}
417418

418-
override executeCallback(
419-
server: Server,
420-
session: ClientSession | undefined,
421-
callback: Callback<Document>
422-
): void {
419+
override async execute(server: Server, session: ClientSession | undefined): Promise<Document> {
423420
const serverWireVersion = maxWireVersion(server);
424421

425422
const cursor = this.options.batchSize ? { batchSize: this.options.batchSize } : {};
@@ -432,7 +429,15 @@ export class ListIndexesOperation extends CommandCallbackOperation<Document> {
432429
command.comment = this.options.comment;
433430
}
434431

435-
super.executeCommandCallback(server, session, command, callback);
432+
return super.executeCommand(server, session, command);
433+
}
434+
435+
protected executeCallback(
436+
_server: Server,
437+
_session: ClientSession | undefined,
438+
_callback: Callback<Document>
439+
): void {
440+
throw new Error('Method not implemented.');
436441
}
437442
}
438443

src/operations/kill_cursors.ts

Lines changed: 11 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,8 @@ import type { Long } from '../bson';
22
import { MongoRuntimeError } from '../error';
33
import type { Server } from '../sdam/server';
44
import type { ClientSession } from '../sessions';
5-
import type { Callback, MongoDBNamespace } from '../utils';
6-
import {
7-
AbstractCallbackOperation,
8-
Aspect,
9-
defineAspects,
10-
type OperationOptions
11-
} from './operation';
5+
import type { MongoDBNamespace } from '../utils';
6+
import { AbstractOperation, Aspect, defineAspects, type OperationOptions } from './operation';
127

138
/**
149
* https://www.mongodb.com/docs/manual/reference/command/killCursors/
@@ -20,7 +15,7 @@ interface KillCursorsCommand {
2015
comment?: unknown;
2116
}
2217

23-
export class KillCursorsOperation extends AbstractCallbackOperation {
18+
export class KillCursorsOperation extends AbstractOperation {
2419
cursorId: Long;
2520

2621
constructor(cursorId: Long, ns: MongoDBNamespace, server: Server, options: OperationOptions) {
@@ -30,32 +25,27 @@ export class KillCursorsOperation extends AbstractCallbackOperation {
3025
this.server = server;
3126
}
3227

33-
executeCallback(
34-
server: Server,
35-
session: ClientSession | undefined,
36-
callback: Callback<void>
37-
): void {
28+
async execute(server: Server, session: ClientSession | undefined): Promise<void> {
3829
if (server !== this.server) {
39-
return callback(
40-
new MongoRuntimeError('Killcursor must run on the same server operation began on')
41-
);
30+
throw new MongoRuntimeError('Killcursor must run on the same server operation began on');
4231
}
4332

4433
const killCursors = this.ns.collection;
4534
if (killCursors == null) {
4635
// Cursors should have adopted the namespace returned by MongoDB
4736
// which should always defined a collection name (even a pseudo one, ex. db.aggregate())
48-
return callback(
49-
new MongoRuntimeError('A collection name must be determined before killCursors')
50-
);
37+
throw new MongoRuntimeError('A collection name must be determined before killCursors');
5138
}
5239

5340
const killCursorsCommand: KillCursorsCommand = {
5441
killCursors,
5542
cursors: [this.cursorId]
5643
};
57-
58-
server.command(this.ns, killCursorsCommand, { session }, () => callback());
44+
try {
45+
await server.commandAsync(this.ns, killCursorsCommand, { session });
46+
} catch {
47+
// The driver should never emit errors from killCursors, this is spec-ed behavior
48+
}
5949
}
6050
}
6151

0 commit comments

Comments
 (0)