diff --git a/src/sql/workbench/services/objectExplorer/browser/objectExplorerService.ts b/src/sql/workbench/services/objectExplorer/browser/objectExplorerService.ts index 851ec2c452..f7b7bcbd7d 100644 --- a/src/sql/workbench/services/objectExplorer/browser/objectExplorerService.ts +++ b/src/sql/workbench/services/objectExplorer/browser/objectExplorerService.ts @@ -23,6 +23,7 @@ import { ServerTreeActionProvider } from 'sql/workbench/services/objectExplorer/ import { ITree } from 'sql/base/parts/tree/browser/tree'; import { AsyncServerTree, ServerTreeElement } from 'sql/workbench/services/objectExplorer/browser/asyncServerTree'; import { mssqlProviderName } from 'sql/platform/connection/common/constants'; +import { ObjectExplorerRequestStatus } from 'sql/workbench/services/objectExplorer/browser/treeSelectionHandler'; export const SERVICE_ID = 'ObjectExplorerService'; @@ -87,7 +88,7 @@ export interface IObjectExplorerService { getObjectExplorerNode(connection: IConnectionProfile): TreeNode | undefined; - updateObjectExplorerNodes(connectionProfile: IConnectionProfile): Promise; + updateObjectExplorerNodes(connectionProfile: IConnectionProfile, requestStatus?: ObjectExplorerRequestStatus | undefined): Promise; deleteObjectExplorerNode(connection: IConnectionProfile): Promise; @@ -208,10 +209,10 @@ export class ObjectExplorerService implements IObjectExplorerService { return this._onSelectionOrFocusChange.event; } - public async updateObjectExplorerNodes(connection: IConnectionProfile): Promise { + public async updateObjectExplorerNodes(connection: IConnectionProfile, requestStatus?: ObjectExplorerRequestStatus | undefined): Promise { const withPassword = await this._connectionManagementService.addSavedPassword(connection); const connectionProfile = ConnectionProfile.fromIConnectionProfile(this._capabilitiesService, withPassword); - return this.updateNewObjectExplorerNode(connectionProfile); + return this.updateNewObjectExplorerNode(connectionProfile, requestStatus); } public async deleteObjectExplorerNode(connection: IConnectionProfile): Promise { @@ -327,12 +328,14 @@ export class ObjectExplorerService implements IObjectExplorerService { this._onUpdateObjectExplorerNodes.fire(eventArgs); } - private async updateNewObjectExplorerNode(connection: ConnectionProfile): Promise { + private async updateNewObjectExplorerNode(connection: ConnectionProfile, requestStatus: ObjectExplorerRequestStatus | undefined): Promise { if (this._activeObjectExplorerNodes[connection.id]) { this.sendUpdateNodeEvent(connection); } else { try { - await this.createNewSession(connection.providerName, connection); + if (!requestStatus) { + await this.createNewSession(connection.providerName, connection); + } } catch (err) { this.sendUpdateNodeEvent(connection, err); throw err; @@ -352,6 +355,7 @@ export class ObjectExplorerService implements IObjectExplorerService { connection: connection, nodes: {} }; + return result; } else { throw new Error(`Provider doesn't exist. id: ${providerId}`); diff --git a/src/sql/workbench/services/objectExplorer/browser/treeSelectionHandler.ts b/src/sql/workbench/services/objectExplorer/browser/treeSelectionHandler.ts index 109962eae0..c3869f8758 100644 --- a/src/sql/workbench/services/objectExplorer/browser/treeSelectionHandler.ts +++ b/src/sql/workbench/services/objectExplorer/browser/treeSelectionHandler.ts @@ -15,12 +15,17 @@ import { AsyncServerTree } from 'sql/workbench/services/objectExplorer/browser/a import { ICapabilitiesService } from 'sql/platform/capabilities/common/capabilitiesService'; import { onUnexpectedError } from 'vs/base/common/errors'; +export interface ObjectExplorerRequestStatus { + inProgress: boolean; +} + export class TreeSelectionHandler { // progressRunner: IProgressRunner; private _lastClicked: any[] | undefined; private _clickTimer: any = undefined; private _otherTimer: any = undefined; + private _requestStatus: ObjectExplorerRequestStatus | undefined = undefined; // constructor(@IProgressService private _progressService: IProgressService) { @@ -50,13 +55,13 @@ export class TreeSelectionHandler { * Handle selection of tree element */ public onTreeSelect(event: any, tree: AsyncServerTree | ITree, connectionManagementService: IConnectionManagementService, objectExplorerService: IObjectExplorerService, capabilitiesService: ICapabilitiesService, connectionCompleteCallback: () => void) { - let sendSelectionEvent = ((event: any, selection: any, isDoubleClick: boolean, userInteraction: boolean) => { + let sendSelectionEvent = ((event: any, selection: any, isDoubleClick: boolean, userInteraction: boolean, requestStatus: ObjectExplorerRequestStatus | undefined = undefined) => { // userInteraction: defensive - don't touch this something else is handling it. if (userInteraction === true && this._lastClicked && this._lastClicked[0] === selection[0]) { this._lastClicked = undefined; } if (!TreeUpdateUtils.isInDragAndDrop) { - this.handleTreeItemSelected(connectionManagementService, objectExplorerService, capabilitiesService, isDoubleClick, this.isKeyboardEvent(event), selection, tree, connectionCompleteCallback); + this.handleTreeItemSelected(connectionManagementService, objectExplorerService, capabilitiesService, isDoubleClick, this.isKeyboardEvent(event), selection, tree, connectionCompleteCallback, requestStatus); } }); @@ -82,12 +87,14 @@ export class TreeSelectionHandler { this._lastClicked = selection; this._clickTimer = setTimeout(() => { + // Sets request status object when timer is executed + this._requestStatus = { inProgress: true }; sendSelectionEvent(event, selection, false, true); }, 400); } else { clearTimeout(this._otherTimer); this._otherTimer = setTimeout(() => { - sendSelectionEvent(event, selection, false, false); + sendSelectionEvent(event, selection, false, false, this._requestStatus); }, 400); } } @@ -102,8 +109,9 @@ export class TreeSelectionHandler { * @param selection * @param tree * @param connectionCompleteCallback A function that gets called after a connection is established due to the selection, if needed + * @param requestStatus Used to identify if a new session should be created or not to avoid creating back to back sessions */ - private handleTreeItemSelected(connectionManagementService: IConnectionManagementService, objectExplorerService: IObjectExplorerService, capabilitiesService: ICapabilitiesService, isDoubleClick: boolean, isKeyboard: boolean, selection: any[], tree: AsyncServerTree | ITree, connectionCompleteCallback: () => void): void { + private handleTreeItemSelected(connectionManagementService: IConnectionManagementService, objectExplorerService: IObjectExplorerService, capabilitiesService: ICapabilitiesService, isDoubleClick: boolean, isKeyboard: boolean, selection: any[], tree: AsyncServerTree | ITree, connectionCompleteCallback: () => void, requestStatus: ObjectExplorerRequestStatus | undefined): void { if (tree instanceof AsyncServerTree) { if (selection && selection.length > 0 && (selection[0] instanceof ConnectionProfile)) { if (!capabilitiesService.getCapabilities(selection[0].providerName)) { @@ -131,7 +139,12 @@ export class TreeSelectionHandler { if (connectionProfile) { this.onTreeActionStateChange(true); - TreeUpdateUtils.connectAndCreateOeSession(connectionProfile, options, connectionManagementService, objectExplorerService, tree).then(sessionCreated => { + TreeUpdateUtils.connectAndCreateOeSession(connectionProfile, options, connectionManagementService, objectExplorerService, tree, requestStatus).then(sessionCreated => { + // Clears request status object that was created when the first timeout callback is executed. + if (this._requestStatus) { + this._requestStatus = undefined; + } + if (!sessionCreated) { this.onTreeActionStateChange(false); } diff --git a/src/sql/workbench/services/objectExplorer/browser/treeUpdateUtils.ts b/src/sql/workbench/services/objectExplorer/browser/treeUpdateUtils.ts index 340a05762e..1f1ce584df 100644 --- a/src/sql/workbench/services/objectExplorer/browser/treeUpdateUtils.ts +++ b/src/sql/workbench/services/objectExplorer/browser/treeUpdateUtils.ts @@ -14,6 +14,7 @@ import { TreeNode } from 'sql/workbench/services/objectExplorer/common/treeNode' import { Disposable, isDisposable } from 'vs/base/common/lifecycle'; import { onUnexpectedError } from 'vs/base/common/errors'; import { AsyncServerTree, ServerTreeElement } from 'sql/workbench/services/objectExplorer/browser/asyncServerTree'; +import { ObjectExplorerRequestStatus } from 'sql/workbench/services/objectExplorer/browser/treeSelectionHandler'; export interface IExpandableTree extends ITree { /** @@ -222,7 +223,7 @@ export class TreeUpdateUtils { * @param objectExplorerService Object explorer service instance */ public static async connectAndCreateOeSession(connection: ConnectionProfile, options: IConnectionCompletionOptions, - connectionManagementService: IConnectionManagementService, objectExplorerService: IObjectExplorerService, tree: AsyncServerTree | ITree | undefined): Promise { + connectionManagementService: IConnectionManagementService, objectExplorerService: IObjectExplorerService, tree: AsyncServerTree | ITree | undefined, requestStatus?: ObjectExplorerRequestStatus | undefined): Promise { const connectedConnection = await TreeUpdateUtils.connectIfNotConnected(connection, options, connectionManagementService, tree); if (connectedConnection) { // append group ID and original display name to build unique OE session ID @@ -231,7 +232,7 @@ export class TreeUpdateUtils { let rootNode: TreeNode | undefined = objectExplorerService.getObjectExplorerNode(connectedConnection); if (!rootNode) { - await objectExplorerService.updateObjectExplorerNodes(connectedConnection); + await objectExplorerService.updateObjectExplorerNodes(connectedConnection, requestStatus); return true; // The oe request is sent. an event will be raised when the session is created } else {