Skip to content

Commit 52853ff

Browse files
authored
feat(NODE-4522)!: remove callback support (#3499)
1 parent 409a095 commit 52853ff

30 files changed

+638
-2064
lines changed

.evergreen/run-tests.sh

+1-1
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,7 @@ else
5252
source "$DRIVERS_TOOLS"/.evergreen/csfle/set-temp-creds.sh
5353
fi
5454

55-
npm install mongodb-client-encryption@">=2.3.0"
55+
npm install mongodb-client-encryption@">=2.4.0-alpha.0"
5656
npm install @mongodb-js/zstd
5757
npm install snappy
5858

etc/notes/CHANGES_5.0.0.md

+48-1
Original file line numberDiff line numberDiff line change
@@ -16,9 +16,56 @@ The following is a detailed collection of the changes in the major v5 release of
1616

1717
## Changes
1818

19+
### Optional callback support migrated to `mongodb-legacy`
20+
21+
If you are a callback user and you are not ready to use promises, support for your workflow has **not** been removed.
22+
We have migrated it to a new package:
23+
24+
- [`mongodb-legacy` Github](https://github.com./mongodb-js/nodejs-mongodb-legacy#readme)
25+
- [`mongodb-legacy` npm](https://www.npmjs.com/package/mongodb-legacy)
26+
27+
The package wraps all of the driver's asynchronous operations that previously supported both promises and callbacks. All the wrapped APIs offer callback support via an optional callback argument alongside a Promise return value so projects with mixed usage will continue to work.
28+
29+
#### Example usage of equivalent callback and promise usage
30+
31+
After installing the package and modifying imports the following example demonstrates equivalent usages of either `async`/`await` syntax, `.then`/`.catch` chaining, or callbacks:
32+
33+
```ts
34+
// Just add '-legacy' to my mongodb import
35+
import { MongoClient } from 'mongodb-legacy';
36+
const client = new MongoClient();
37+
const db = client.db();
38+
const collection = db.collection('pets');
39+
40+
// Legacy projects may have intermixed API usage:
41+
app.get('/endpoint_async_await', async (req, res) => {
42+
try {
43+
const result = await collection.findOne({})
44+
res.end(JSON.stringify(result));
45+
} catch (error) {
46+
res.errorHandling(error)
47+
}
48+
});
49+
50+
app.get('/endpoint_promises', (req, res) => {
51+
collection
52+
.findOne({})
53+
.then(result => res.end(JSON.stringify(result)))
54+
.catch(error => res.errorHandling(error));
55+
});
56+
57+
app.get('/endpoint_callbacks', (req, res) => {
58+
collection.findOne({}, (error, result) => {
59+
if (error) return res.errorHandling(error);
60+
res.end(JSON.stringify(result));
61+
});
62+
});
63+
```
64+
65+
1966
### Dot Notation Typescript Support Removed By Default
2067

21-
**NOTE** This is a **Typescript compile-time only** change. Dot notation in filters sent to MongoDB will still work the same.
68+
**NOTE:** This is a **Typescript compile-time only** change. Dot notation in filters sent to MongoDB will still work the same.
2269

2370
Version 4.3.0 introduced Typescript support for dot notation in filter predicates. For example:
2471

src/admin.ts

+33-184
Large diffs are not rendered by default.

src/bulk/common.ts

+25-48
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,6 @@ import {
2323
Callback,
2424
getTopology,
2525
hasAtomicOperators,
26-
maybeCallback,
2726
MongoDBNamespace,
2827
resolveOptions
2928
} from '../utils';
@@ -1175,56 +1174,34 @@ export abstract class BulkOperationBase {
11751174
return batches;
11761175
}
11771176

1178-
execute(options?: BulkWriteOptions): Promise<BulkWriteResult>;
1179-
/** @deprecated Callbacks are deprecated and will be removed in the next major version. See [mongodb-legacy](https://github.com./mongodb-js/nodejs-mongodb-legacy) for migration assistance */
1180-
execute(callback: Callback<BulkWriteResult>): void;
1181-
/** @deprecated Callbacks are deprecated and will be removed in the next major version. See [mongodb-legacy](https://github.com./mongodb-js/nodejs-mongodb-legacy) for migration assistance */
1182-
execute(options: BulkWriteOptions | undefined, callback: Callback<BulkWriteResult>): void;
1183-
/** @deprecated Callbacks are deprecated and will be removed in the next major version. See [mongodb-legacy](https://github.com./mongodb-js/nodejs-mongodb-legacy) for migration assistance */
1184-
execute(
1185-
options?: BulkWriteOptions | Callback<BulkWriteResult>,
1186-
callback?: Callback<BulkWriteResult>
1187-
): Promise<BulkWriteResult> | void;
1188-
execute(
1189-
options?: BulkWriteOptions | Callback<BulkWriteResult>,
1190-
callback?: Callback<BulkWriteResult>
1191-
): Promise<BulkWriteResult> | void {
1192-
callback =
1193-
typeof callback === 'function'
1194-
? callback
1195-
: typeof options === 'function'
1196-
? options
1197-
: undefined;
1198-
return maybeCallback(async () => {
1199-
options = options != null && typeof options !== 'function' ? options : {};
1200-
1201-
if (this.s.executed) {
1202-
throw new MongoBatchReExecutionError();
1203-
}
1177+
async execute(options: BulkWriteOptions = {}): Promise<BulkWriteResult> {
1178+
if (this.s.executed) {
1179+
throw new MongoBatchReExecutionError();
1180+
}
12041181

1205-
const writeConcern = WriteConcern.fromOptions(options);
1206-
if (writeConcern) {
1207-
this.s.writeConcern = writeConcern;
1208-
}
1182+
const writeConcern = WriteConcern.fromOptions(options);
1183+
if (writeConcern) {
1184+
this.s.writeConcern = writeConcern;
1185+
}
12091186

1210-
// If we have current batch
1211-
if (this.isOrdered) {
1212-
if (this.s.currentBatch) this.s.batches.push(this.s.currentBatch);
1213-
} else {
1214-
if (this.s.currentInsertBatch) this.s.batches.push(this.s.currentInsertBatch);
1215-
if (this.s.currentUpdateBatch) this.s.batches.push(this.s.currentUpdateBatch);
1216-
if (this.s.currentRemoveBatch) this.s.batches.push(this.s.currentRemoveBatch);
1217-
}
1218-
// If we have no operations in the bulk raise an error
1219-
if (this.s.batches.length === 0) {
1220-
throw new MongoInvalidArgumentError('Invalid BulkOperation, Batch cannot be empty');
1221-
}
1187+
// If we have current batch
1188+
if (this.isOrdered) {
1189+
if (this.s.currentBatch) this.s.batches.push(this.s.currentBatch);
1190+
} else {
1191+
if (this.s.currentInsertBatch) this.s.batches.push(this.s.currentInsertBatch);
1192+
if (this.s.currentUpdateBatch) this.s.batches.push(this.s.currentUpdateBatch);
1193+
if (this.s.currentRemoveBatch) this.s.batches.push(this.s.currentRemoveBatch);
1194+
}
1195+
// If we have no operations in the bulk raise an error
1196+
if (this.s.batches.length === 0) {
1197+
throw new MongoInvalidArgumentError('Invalid BulkOperation, Batch cannot be empty');
1198+
}
1199+
1200+
this.s.executed = true;
1201+
const finalOptions = { ...this.s.options, ...options };
1202+
const operation = new BulkWriteShimOperation(this, finalOptions);
12221203

1223-
this.s.executed = true;
1224-
const finalOptions = { ...this.s.options, ...options };
1225-
const operation = new BulkWriteShimOperation(this, finalOptions);
1226-
return executeOperation(this.s.collection.s.db.s.client, operation);
1227-
}, callback);
1204+
return executeOperation(this.s.collection.s.db.s.client, operation);
12281205
}
12291206

12301207
/**

src/change_stream.ts

+58-80
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ import type { AggregateOptions } from './operations/aggregate';
1919
import type { CollationOptions, OperationParent } from './operations/command';
2020
import type { ReadPreference } from './read_preference';
2121
import type { ServerSessionId } from './sessions';
22-
import { Callback, filterOptions, getTopology, maybeCallback, MongoDBNamespace } from './utils';
22+
import { filterOptions, getTopology, MongoDBNamespace } from './utils';
2323

2424
/** @internal */
2525
const kCursorStream = Symbol('cursorStream');
@@ -637,99 +637,84 @@ export class ChangeStream<
637637
}
638638

639639
/** Check if there is any document still available in the Change Stream */
640-
hasNext(): Promise<boolean>;
641-
/** @deprecated Callbacks are deprecated and will be removed in the next major version. See [mongodb-legacy](https://github.com./mongodb-js/nodejs-mongodb-legacy) for migration assistance */
642-
hasNext(callback: Callback<boolean>): void;
643-
hasNext(callback?: Callback): Promise<boolean> | void {
640+
async hasNext(): Promise<boolean> {
644641
this._setIsIterator();
645-
return maybeCallback(async () => {
646-
// Change streams must resume indefinitely while each resume event succeeds.
647-
// This loop continues until either a change event is received or until a resume attempt
648-
// fails.
649-
// eslint-disable-next-line no-constant-condition
650-
while (true) {
642+
// Change streams must resume indefinitely while each resume event succeeds.
643+
// This loop continues until either a change event is received or until a resume attempt
644+
// fails.
645+
// eslint-disable-next-line no-constant-condition
646+
while (true) {
647+
try {
648+
const hasNext = await this.cursor.hasNext();
649+
return hasNext;
650+
} catch (error) {
651651
try {
652-
const hasNext = await this.cursor.hasNext();
653-
return hasNext;
652+
await this._processErrorIteratorMode(error);
654653
} catch (error) {
655654
try {
656-
await this._processErrorIteratorMode(error);
657-
} catch (error) {
658-
try {
659-
await this.close();
660-
} catch {
661-
// We are not concerned with errors from close()
662-
}
663-
throw error;
655+
await this.close();
656+
} catch {
657+
// We are not concerned with errors from close()
664658
}
659+
throw error;
665660
}
666661
}
667-
}, callback);
662+
}
668663
}
669664

670665
/** Get the next available document from the Change Stream. */
671-
next(): Promise<TChange>;
672-
/** @deprecated Callbacks are deprecated and will be removed in the next major version. See [mongodb-legacy](https://github.com./mongodb-js/nodejs-mongodb-legacy) for migration assistance */
673-
next(callback: Callback<TChange>): void;
674-
next(callback?: Callback<TChange>): Promise<TChange> | void {
666+
async next(): Promise<TChange> {
675667
this._setIsIterator();
676-
return maybeCallback(async () => {
677-
// Change streams must resume indefinitely while each resume event succeeds.
678-
// This loop continues until either a change event is received or until a resume attempt
679-
// fails.
680-
// eslint-disable-next-line no-constant-condition
681-
while (true) {
668+
// Change streams must resume indefinitely while each resume event succeeds.
669+
// This loop continues until either a change event is received or until a resume attempt
670+
// fails.
671+
// eslint-disable-next-line no-constant-condition
672+
while (true) {
673+
try {
674+
const change = await this.cursor.next();
675+
const processedChange = this._processChange(change ?? null);
676+
return processedChange;
677+
} catch (error) {
682678
try {
683-
const change = await this.cursor.next();
684-
const processedChange = this._processChange(change ?? null);
685-
return processedChange;
679+
await this._processErrorIteratorMode(error);
686680
} catch (error) {
687681
try {
688-
await this._processErrorIteratorMode(error);
689-
} catch (error) {
690-
try {
691-
await this.close();
692-
} catch {
693-
// We are not concerned with errors from close()
694-
}
695-
throw error;
682+
await this.close();
683+
} catch {
684+
// We are not concerned with errors from close()
696685
}
686+
throw error;
697687
}
698688
}
699-
}, callback);
689+
}
700690
}
701691

702692
/**
703693
* Try to get the next available document from the Change Stream's cursor or `null` if an empty batch is returned
704694
*/
705-
tryNext(): Promise<Document | null>;
706-
/** @deprecated Callbacks are deprecated and will be removed in the next major version. See [mongodb-legacy](https://github.com./mongodb-js/nodejs-mongodb-legacy) for migration assistance */
707-
tryNext(callback: Callback<Document | null>): void;
708-
tryNext(callback?: Callback<Document | null>): Promise<Document | null> | void {
695+
async tryNext(): Promise<Document | null> {
709696
this._setIsIterator();
710-
return maybeCallback(async () => {
711-
// Change streams must resume indefinitely while each resume event succeeds.
712-
// This loop continues until either a change event is received or until a resume attempt
713-
// fails.
714-
// eslint-disable-next-line no-constant-condition
715-
while (true) {
697+
// Change streams must resume indefinitely while each resume event succeeds.
698+
// This loop continues until either a change event is received or until a resume attempt
699+
// fails.
700+
// eslint-disable-next-line no-constant-condition
701+
while (true) {
702+
try {
703+
const change = await this.cursor.tryNext();
704+
return change ?? null;
705+
} catch (error) {
716706
try {
717-
const change = await this.cursor.tryNext();
718-
return change ?? null;
707+
await this._processErrorIteratorMode(error);
719708
} catch (error) {
720709
try {
721-
await this._processErrorIteratorMode(error);
722-
} catch (error) {
723-
try {
724-
await this.close();
725-
} catch {
726-
// We are not concerned with errors from close()
727-
}
728-
throw error;
710+
await this.close();
711+
} catch {
712+
// We are not concerned with errors from close()
729713
}
714+
throw error;
730715
}
731716
}
732-
}, callback);
717+
}
733718
}
734719

735720
async *[Symbol.asyncIterator](): AsyncGenerator<TChange, void, void> {
@@ -758,20 +743,15 @@ export class ChangeStream<
758743
}
759744

760745
/** Close the Change Stream */
761-
close(): Promise<void>;
762-
/** @deprecated Callbacks are deprecated and will be removed in the next major version. See [mongodb-legacy](https://github.com./mongodb-js/nodejs-mongodb-legacy) for migration assistance */
763-
close(callback: Callback): void;
764-
close(callback?: Callback): Promise<void> | void {
746+
async close(): Promise<void> {
765747
this[kClosed] = true;
766748

767-
return maybeCallback(async () => {
768-
const cursor = this.cursor;
769-
try {
770-
await cursor.close();
771-
} finally {
772-
this._endStream();
773-
}
774-
}, callback);
749+
const cursor = this.cursor;
750+
try {
751+
await cursor.close();
752+
} finally {
753+
this._endStream();
754+
}
775755
}
776756

777757
/**
@@ -864,9 +844,7 @@ export class ChangeStream<
864844
private _closeEmitterModeWithError(error: AnyError): void {
865845
this.emit(ChangeStream.ERROR, error);
866846

867-
this.close(() => {
868-
// nothing to do
869-
});
847+
this.close().catch(() => null);
870848
}
871849

872850
/** @internal */

0 commit comments

Comments
 (0)