Skip to content

feat(NODE-4522)!: remove callback support #3499

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 37 commits into from
Jan 23, 2023
Merged
Changes from 1 commit
Commits
Show all changes
37 commits
Select commit Hold shift + click to select a range
17e87cf
test(NODE-4919): import mongodb-legacy in tests
nbbeeken Jan 4, 2023
82ac2b9
move test file, update coverage
nbbeeken Jan 19, 2023
4ac5a2f
test(NODE-4919): import mongodb-legacy in tests
nbbeeken Jan 4, 2023
d71a302
feat: remove callbacks from admin.ts
nbbeeken Dec 20, 2022
b1d5971
feat: remove callbacks from bulk/common.ts
nbbeeken Dec 20, 2022
af01cc7
feat: remove callbacks from change_stream.ts
nbbeeken Dec 20, 2022
28d2093
feat: remove callbacks from collection.ts
nbbeeken Dec 20, 2022
16ee978
feat: remove callbacks from cursors
nbbeeken Dec 20, 2022
5623462
feat: remove callbacks from db.ts
nbbeeken Dec 20, 2022
53ad68e
feat: remove callbacks from mongo_client.ts
nbbeeken Dec 20, 2022
51b0649
feat: remove callbacks from sessions.ts
nbbeeken Dec 20, 2022
bfaf3c9
feat: remove callbacks from gridfs
nbbeeken Dec 20, 2022
217101d
async keyword withTransaction and agg.explain
nbbeeken Jan 19, 2023
ffcd2d8
fle: bump to promise first fle commit
nbbeeken Jan 20, 2023
5f43565
fix: withSession
nbbeeken Jan 20, 2023
c3f2769
rm test file
nbbeeken Jan 20, 2023
4d67771
fix: lint
nbbeeken Jan 20, 2023
7a015e5
fix and skip tests
nbbeeken Jan 20, 2023
84f4f9a
jira ticket
nbbeeken Jan 20, 2023
7c06196
fix: await things
nbbeeken Jan 20, 2023
c1d5738
flip messages to check
nbbeeken Jan 20, 2023
266ec54
use fle alpha
nbbeeken Jan 20, 2023
2a1ee77
round one: fight
nbbeeken Jan 20, 2023
c12c401
comments
nbbeeken Jan 21, 2023
fb12143
consistent default arguments
nbbeeken Jan 21, 2023
e0b9538
fix tests, migration
nbbeeken Jan 21, 2023
43a0dbb
strangely close returns undefined
nbbeeken Jan 21, 2023
1ee4f9a
fix test
nbbeeken Jan 21, 2023
66a8942
Merge branch 'main' into NODE-4522-rm-callbacks
nbbeeken Jan 21, 2023
a11d175
argument defaulting fix
nbbeeken Jan 21, 2023
cad2804
fixup test
nbbeeken Jan 21, 2023
5b7dc71
fix async function strangeness
nbbeeken Jan 22, 2023
b489aca
handle close error
nbbeeken Jan 23, 2023
c0b36a9
migration suggestions
nbbeeken Jan 23, 2023
b91886c
rm todos
nbbeeken Jan 23, 2023
b0fda7d
Merge branch 'main' into NODE-4522-rm-callbacks
nbbeeken Jan 23, 2023
5c54370
Merge branch 'main' into NODE-4522-rm-callbacks
nbbeeken Jan 23, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
feat: remove callbacks from mongo_client.ts
  • Loading branch information
nbbeeken committed Jan 20, 2023
commit 53ad68e7703d764605a5bb7650e9259a5f9287f7
206 changes: 78 additions & 128 deletions src/mongo_client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,6 @@ import type { SrvPoller } from './sdam/srv_polling';
import { Topology, TopologyEvents } from './sdam/topology';
import { ClientSession, ClientSessionOptions, ServerSessionPool } from './sessions';
import {
Callback,
ClientMetadata,
HostAddress,
maybeCallback,
Expand Down Expand Up @@ -425,79 +424,60 @@ export class MongoClient extends TypedEventEmitter<MongoClientEvents> {
*
* @see docs.mongodb.org/manual/reference/connection-string/
*/
connect(): Promise<this>;
/** @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 */
connect(callback: Callback<this>): void;
connect(callback?: Callback<this>): Promise<this> | void {
if (callback && typeof callback !== 'function') {
throw new MongoInvalidArgumentError('Method `connect` only accepts a callback');
async connect(): Promise<this> {
if (this.topology && this.topology.isConnected()) {
return this;
}

return maybeCallback(async () => {
if (this.topology && this.topology.isConnected()) {
return this;
}

const options = this[kOptions];
const options = this[kOptions];

if (typeof options.srvHost === 'string') {
const hosts = await resolveSRVRecord(options);
if (typeof options.srvHost === 'string') {
const hosts = await resolveSRVRecord(options);

for (const [index, host] of hosts.entries()) {
options.hosts[index] = host;
}
for (const [index, host] of hosts.entries()) {
options.hosts[index] = host;
}
}

const topology = new Topology(options.hosts, options);
// Events can be emitted before initialization is complete so we have to
// save the reference to the topology on the client ASAP if the event handlers need to access it
this.topology = topology;
topology.client = this;
const topology = new Topology(options.hosts, options);
// Events can be emitted before initialization is complete so we have to
// save the reference to the topology on the client ASAP if the event handlers need to access it
this.topology = topology;
topology.client = this;

topology.once(Topology.OPEN, () => this.emit('open', this));
topology.once(Topology.OPEN, () => this.emit('open', this));

for (const event of MONGO_CLIENT_EVENTS) {
topology.on(event, (...args: any[]) => this.emit(event, ...(args as any)));
}
for (const event of MONGO_CLIENT_EVENTS) {
topology.on(event, (...args: any[]) => this.emit(event, ...(args as any)));
}

const topologyConnect = async () => {
try {
await promisify(callback => topology.connect(options, callback))();
} catch (error) {
topology.close({ force: true });
throw error;
}
};

if (this.autoEncrypter) {
const initAutoEncrypter = promisify(callback => this.autoEncrypter?.init(callback));
await initAutoEncrypter();
await topologyConnect();
await options.encrypter.connectInternalClient();
} else {
await topologyConnect();
const topologyConnect = async () => {
try {
await promisify(callback => topology.connect(options, callback))();
} catch (error) {
topology.close({ force: true });
throw error;
}
};

return this;
}, callback);
if (this.autoEncrypter) {
const initAutoEncrypter = promisify(callback => this.autoEncrypter?.init(callback));
await initAutoEncrypter();
await topologyConnect();
await options.encrypter.connectInternalClient();
} else {
await topologyConnect();
}

return this;
}

/**
* Close the db and its underlying connections
* Close the client and its underlying connections
*
* @param force - Force close, emitting no events
* @param callback - An optional callback, a Promise will be returned if none is provided
*/
close(): Promise<void>;
close(force: boolean): Promise<void>;
/** @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 */
close(callback: Callback<void>): void;
/** @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 */
close(force: boolean, callback: Callback<void>): void;
close(
forceOrCallback?: boolean | Callback<void>,
callback?: Callback<void>
): Promise<void> | void {
async close(force = false): Promise<void> {
// There's no way to set hasBeenClosed back to false
Object.defineProperty(this.s, 'hasBeenClosed', {
value: true,
Expand All @@ -506,58 +486,50 @@ export class MongoClient extends TypedEventEmitter<MongoClientEvents> {
writable: false
});

if (typeof forceOrCallback === 'function') {
callback = forceOrCallback;
}

const force = typeof forceOrCallback === 'boolean' ? forceOrCallback : false;
const activeSessionEnds = Array.from(this.s.activeSessions, session => session.endSession());
this.s.activeSessions.clear();

return maybeCallback(async () => {
const activeSessionEnds = Array.from(this.s.activeSessions, session => session.endSession());
this.s.activeSessions.clear();
await Promise.all(activeSessionEnds);

await Promise.all(activeSessionEnds);
if (this.topology == null) {
return;
}

if (this.topology == null) {
return;
// If we would attempt to select a server and get nothing back we short circuit
// to avoid the server selection timeout.
const selector = readPreferenceServerSelector(ReadPreference.primaryPreferred);
const topologyDescription = this.topology.description;
const serverDescriptions = Array.from(topologyDescription.servers.values());
const servers = selector(topologyDescription, serverDescriptions);
if (servers.length !== 0) {
const endSessions = Array.from(this.s.sessionPool.sessions, ({ id }) => id);
if (endSessions.length !== 0) {
await this.db('admin')
.command(
{ endSessions },
{ readPreference: ReadPreference.primaryPreferred, noResponse: true }
)
.catch(() => null); // outcome does not matter
}
}

// If we would attempt to select a server and get nothing back we short circuit
// to avoid the server selection timeout.
const selector = readPreferenceServerSelector(ReadPreference.primaryPreferred);
const topologyDescription = this.topology.description;
const serverDescriptions = Array.from(topologyDescription.servers.values());
const servers = selector(topologyDescription, serverDescriptions);
if (servers.length !== 0) {
const endSessions = Array.from(this.s.sessionPool.sessions, ({ id }) => id);
if (endSessions.length !== 0) {
await this.db('admin')
.command(
{ endSessions },
{ readPreference: ReadPreference.primaryPreferred, noResponse: true }
)
.catch(() => null); // outcome does not matter
// clear out references to old topology
const topology = this.topology;
this.topology = undefined;

await new Promise<void>((resolve, reject) => {
topology.close({ force }, error => {
if (error) return reject(error);
const { encrypter } = this[kOptions];
if (encrypter) {
return encrypter.close(this, force, error => {
if (error) return reject(error);
resolve();
});
}
}

// clear out references to old topology
const topology = this.topology;
this.topology = undefined;

await new Promise<void>((resolve, reject) => {
topology.close({ force }, error => {
if (error) return reject(error);
const { encrypter } = this[kOptions];
if (encrypter) {
return encrypter.close(this, force, error => {
if (error) return reject(error);
resolve();
});
}
resolve();
});
resolve();
});
}, callback);
});
}

/**
Expand Down Expand Up @@ -592,34 +564,12 @@ export class MongoClient extends TypedEventEmitter<MongoClientEvents> {
*
* @see https://docs.mongodb.org/manual/reference/connection-string/
*/
static connect(url: string): Promise<MongoClient>;
static connect(url: string, options: MongoClientOptions): Promise<MongoClient>;
/** @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 */
static connect(url: string, callback: Callback<MongoClient>): void;
/** @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 */
static connect(url: string, options: MongoClientOptions, callback: Callback<MongoClient>): void;
static connect(
url: string,
options?: MongoClientOptions | Callback<MongoClient>,
callback?: Callback<MongoClient>
): Promise<MongoClient> | void {
callback =
typeof callback === 'function'
? callback
: typeof options === 'function'
? options
: undefined;

return maybeCallback(async () => {
options = typeof options !== 'function' ? options : undefined;
const client = new this(url, options);
return client.connect();
}, callback);
static async connect(url: string, options?: MongoClientOptions): Promise<MongoClient> {
const client = new this(url, options);
return client.connect();
}

/** Starts a new session on the server */
startSession(): ClientSession;
startSession(options: ClientSessionOptions): ClientSession;
startSession(options?: ClientSessionOptions): ClientSession {
const session = new ClientSession(
this,
Expand All @@ -646,7 +596,7 @@ export class MongoClient extends TypedEventEmitter<MongoClientEvents> {
withSession(callback: WithSessionCallback): Promise<void>;
withSession(options: ClientSessionOptions, callback: WithSessionCallback): Promise<void>;
withSession(
optionsOrOperation?: ClientSessionOptions | WithSessionCallback,
optionsOrOperation: ClientSessionOptions | WithSessionCallback,
callback?: WithSessionCallback
): Promise<void> {
const options = {
Expand Down