Skip to content

Commit 18f230d

Browse files
authored
VSCODE-155: Allow connecting to a new connection when already connecting (#156)
1 parent 814dc4c commit 18f230d

File tree

11 files changed

+882
-867
lines changed

11 files changed

+882
-867
lines changed

src/connectionController.ts

Lines changed: 66 additions & 110 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,6 @@ const MAX_CONNECTION_NAME_LENGTH = 512;
2020
export enum DataServiceEventTypes {
2121
CONNECTIONS_DID_CHANGE = 'CONNECTIONS_DID_CHANGE',
2222
ACTIVE_CONNECTION_CHANGED = 'ACTIVE_CONNECTION_CHANGED',
23-
ACTIVE_CONNECTION_CHANGING = 'ACTIVE_CONNECTION_CHANGING'
2423
}
2524

2625
export enum ConnectionTypes {
@@ -55,6 +54,12 @@ export default class ConnectionController {
5554
_activeConnectionModel: null | ConnectionModelType = null;
5655
private _currentConnectionId: null | string = null;
5756

57+
// When we are connecting to a server we save a connection version to
58+
// the request. That way if a new connection attempt is made while
59+
// the connection is being established, we know we can ignore the
60+
// request when it is completed so we don't have two live connections at once.
61+
private _connectingVersion = 0;
62+
5863
private _connecting = false;
5964
private _connectingConnectionId: null | string = null;
6065
private _disconnecting = false;
@@ -231,32 +236,24 @@ export default class ConnectionController {
231236
public sendTelemetry(
232237
newDataService: DataServiceType,
233238
connectionType: ConnectionTypes
234-
) {
239+
): void {
235240
// Send metrics to Segment
236241
this._telemetryController.trackNewConnection(
237242
newDataService,
238243
connectionType
239244
);
240245
}
241246

242-
public parseNewConnectionAndConnect = (
247+
public parseNewConnection = (
243248
newConnectionModel: ConnectionModelType
244-
): Promise<boolean> => {
249+
): ConnectionModelType => {
245250
// Here we re-parse the connection, as it can be loaded from storage or
246251
// passed by the connection model without the class methods.
247-
let connectionModel: ConnectionModelType;
248-
249-
try {
250-
connectionModel = new Connection(newConnectionModel);
251-
} catch (error) {
252-
vscode.window.showErrorMessage(`Unable to load connection: ${error}`);
253-
return Promise.reject(new Error(`Unable to load connection: ${error}`));
254-
}
255-
256-
return this.saveNewConnectionAndConnect(
257-
connectionModel,
258-
ConnectionTypes.CONNECTION_FORM
252+
const connectionModel: ConnectionModelType = new Connection(
253+
newConnectionModel
259254
);
255+
256+
return connectionModel;
260257
};
261258

262259
public saveNewConnectionAndConnect = async (
@@ -330,27 +327,19 @@ export default class ConnectionController {
330327
}).instanceId
331328
);
332329

333-
if (this._connecting) {
334-
return Promise.reject(
335-
new Error('Unable to connect: already connecting.')
336-
);
337-
}
338-
339-
if (this._disconnecting) {
340-
return Promise.reject(
341-
new Error('Unable to connect: currently disconnecting.')
342-
);
343-
}
344-
345-
if (this._activeDataService) {
346-
await this.disconnect();
347-
}
330+
// Store a version of this connection, so we can see when the conection
331+
// is successful if it is still the most recent connection attempt.
332+
this._connectingVersion++;
333+
const connectingAttemptVersion = this._connectingVersion;
348334

349335
this._connecting = true;
350336
this._connectingConnectionId = connectionId;
351337

352338
this.eventEmitter.emit(DataServiceEventTypes.CONNECTIONS_DID_CHANGE);
353-
this.eventEmitter.emit(DataServiceEventTypes.ACTIVE_CONNECTION_CHANGING);
339+
340+
if (this._activeDataService) {
341+
await this.disconnect();
342+
}
354343

355344
this._statusView.showMessage('Connecting to MongoDB...');
356345

@@ -361,6 +350,22 @@ export default class ConnectionController {
361350
const newDataService: DataServiceType = new DataService(connectionModel);
362351

363352
newDataService.connect((err: Error | undefined) => {
353+
if (
354+
connectingAttemptVersion !== this._connectingVersion ||
355+
!this._connections[connectionId]
356+
) {
357+
// If the current attempt is no longer the most recent attempt
358+
// or the connection no longer exists we silently end the connection
359+
// and return.
360+
try {
361+
newDataService.disconnect(() => {});
362+
} catch (e) {
363+
/* */
364+
}
365+
366+
return resolve(false);
367+
}
368+
364369
this._statusView.hideMessage();
365370

366371
if (err) {
@@ -392,7 +397,7 @@ export default class ConnectionController {
392397

393398
public connectWithConnectionId = (connectionId: string): Promise<boolean> => {
394399
if (this._connections[connectionId]) {
395-
let connectionModel: any;
400+
let connectionModel: ConnectionModelType;
396401

397402
try {
398403
const savedConnectionModel = this._connections[connectionId]
@@ -433,36 +438,28 @@ export default class ConnectionController {
433438
this._currentConnectionId
434439
);
435440

436-
if (this._disconnecting) {
441+
if (!this._activeDataService) {
437442
vscode.window.showErrorMessage(
438-
'Unable to disconnect: already disconnecting from an instance.'
443+
'Unable to disconnect: no active connection.'
439444
);
440445

441446
return Promise.resolve(false);
442447
}
443448

444-
if (this._connecting) {
445-
// TODO: The desired UX here may be for the connection to be interrupted.
446-
vscode.window.showErrorMessage(
447-
'Unable to disconnect: currently connecting to an instance.'
448-
);
449+
const dataServiceToDisconnectFrom = this._activeDataService;
449450

450-
return Promise.resolve(false);
451-
}
451+
this._activeDataService = null;
452+
this._currentConnectionId = null;
453+
this._activeConnectionModel = null;
454+
this._disconnecting = true;
455+
456+
this.eventEmitter.emit(DataServiceEventTypes.CONNECTIONS_DID_CHANGE);
457+
this.eventEmitter.emit(DataServiceEventTypes.ACTIVE_CONNECTION_CHANGED);
452458

453459
// Disconnect from the active connection.
454460
return new Promise<boolean>((resolve) => {
455-
if (!this._activeDataService) {
456-
vscode.window.showErrorMessage(
457-
'Unable to disconnect: no active connection.'
458-
);
459-
460-
return resolve(false);
461-
}
462-
463-
this._disconnecting = true;
464461
this._statusView.showMessage('Disconnecting from current connection...');
465-
this._activeDataService.disconnect((err: Error | undefined): void => {
462+
dataServiceToDisconnectFrom.disconnect((err: Error | undefined): void => {
466463
if (err) {
467464
// Show an error, however we still reset the active connection to free up the extension.
468465
vscode.window.showErrorMessage(
@@ -471,17 +468,9 @@ export default class ConnectionController {
471468
} else {
472469
vscode.window.showInformationMessage('MongoDB disconnected.');
473470
}
474-
475-
this._activeDataService = null;
476-
this._currentConnectionId = null;
477-
this._activeConnectionModel = null;
478-
479471
this._disconnecting = false;
480472
this._statusView.hideMessage();
481473

482-
this.eventEmitter.emit(DataServiceEventTypes.CONNECTIONS_DID_CHANGE);
483-
this.eventEmitter.emit(DataServiceEventTypes.ACTIVE_CONNECTION_CHANGED);
484-
485474
return resolve(true);
486475
});
487476
});
@@ -506,24 +495,6 @@ export default class ConnectionController {
506495

507496
// Prompts the user to remove the connection then removes it on affirmation.
508497
public async removeMongoDBConnection(connectionId: string): Promise<boolean> {
509-
// Ensure we aren't currently connecting.
510-
if (this._connecting) {
511-
vscode.window.showErrorMessage(
512-
'Unable to remove connection: currently connecting.'
513-
);
514-
515-
return Promise.resolve(false);
516-
}
517-
518-
// Ensure we aren't currently disconnecting.
519-
if (this._disconnecting) {
520-
vscode.window.showErrorMessage(
521-
'Unable to remove connection: currently disconnecting.'
522-
);
523-
524-
return Promise.resolve(false);
525-
}
526-
527498
if (!this._connections[connectionId]) {
528499
// No active connection(s) to remove.
529500
vscode.window.showErrorMessage('Connection does not exist.');
@@ -545,6 +516,11 @@ export default class ConnectionController {
545516
await this.disconnect();
546517
}
547518

519+
if (!this._connections[connectionId]) {
520+
// If the connection was removed while we were disconnecting we resolve.
521+
return Promise.resolve(false);
522+
}
523+
548524
await this.removeSavedConnection(connectionId);
549525

550526
vscode.window.showInformationMessage('MongoDB connection removed.');
@@ -555,24 +531,6 @@ export default class ConnectionController {
555531
public async onRemoveMongoDBConnection(): Promise<boolean> {
556532
log.info('mdb.removeConnection command called');
557533

558-
// Ensure we aren't currently connecting.
559-
if (this._connecting) {
560-
vscode.window.showErrorMessage(
561-
'Unable to remove connection: currently connecting.'
562-
);
563-
564-
return Promise.resolve(false);
565-
}
566-
567-
// Ensure we aren't currently disconnecting.
568-
if (this._disconnecting) {
569-
vscode.window.showErrorMessage(
570-
'Unable to remove connection: currently disconnecting.'
571-
);
572-
573-
return Promise.resolve(false);
574-
}
575-
576534
const connectionIds = Object.keys(this._connections);
577535

578536
if (connectionIds.length === 0) {
@@ -591,13 +549,13 @@ export default class ConnectionController {
591549
const connectionNameToRemove:
592550
| string
593551
| undefined = await vscode.window.showQuickPick(
594-
connectionIds.map(
595-
(id, index) => `${index + 1}: ${this._connections[id].name}`
596-
),
597-
{
598-
placeHolder: 'Choose a connection to remove...'
599-
}
600-
);
552+
connectionIds.map(
553+
(id, index) => `${index + 1}: ${this._connections[id].name}`
554+
),
555+
{
556+
placeHolder: 'Choose a connection to remove...'
557+
}
558+
);
601559

602560
if (!connectionNameToRemove) {
603561
return Promise.resolve(false);
@@ -729,13 +687,6 @@ export default class ConnectionController {
729687

730688
return !!this._connections[connectionId];
731689
}
732-
public getConnectingConnectionName(): string | null {
733-
if (this._connectingConnectionId === null) {
734-
return null;
735-
}
736-
737-
return this._connections[this._connectingConnectionId].name;
738-
}
739690
public getConnectingConnectionId(): string | null {
740691
return this._connectingConnectionId;
741692
}
@@ -765,6 +716,11 @@ export default class ConnectionController {
765716
this._connecting = false;
766717
this._disconnecting = false;
767718
this._connectingConnectionId = '';
719+
this._connectingVersion = 0;
720+
}
721+
722+
public getConnectingVersion(): number {
723+
return this._connectingVersion;
768724
}
769725

770726
public setActiveConnection(newActiveConnection: any): void {

src/explorer/mdbConnectionsTreeItem.ts

Lines changed: 0 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -88,28 +88,6 @@ export default class MDBConnectionsTreeItem extends vscode.TreeItem
8888
);
8989
});
9090

91-
if (
92-
this._connectionController.isConnecting() &&
93-
!this._connectionController.isConnectionWithIdSaved(
94-
this._connectionController.getConnectingConnectionId()
95-
)
96-
) {
97-
const notYetEstablishConnectionTreeItem = new vscode.TreeItem(
98-
this._connectionController.getConnectingConnectionName() ||
99-
'New Connection'
100-
);
101-
102-
notYetEstablishConnectionTreeItem.description = 'connecting...';
103-
104-
// When we're connecting to a new connection we add simple node showing the connecting status.
105-
return Promise.resolve(
106-
sortTreeItemsByLabel([
107-
...Object.values(this._connectionTreeItems),
108-
notYetEstablishConnectionTreeItem
109-
])
110-
);
111-
}
112-
11391
return Promise.resolve(
11492
sortTreeItemsByLabel(Object.values(this._connectionTreeItems))
11593
);

0 commit comments

Comments
 (0)