From f60bd1335c6527b3fd3135bed7a79aaf87f48c77 Mon Sep 17 00:00:00 2001 From: Aasim Khan Date: Tue, 28 Mar 2023 18:36:45 -0700 Subject: [PATCH] Adding promises and operation timeouts to fix race conditions and infinite loading in OE (#22475) * Adding promises and operation timeouts to fix race conditions * cleaning up logic * Update src/sql/workbench/services/objectExplorer/browser/objectExplorerService.ts Co-authored-by: Charles Gagnon * Update src/sql/workbench/services/objectExplorer/browser/objectExplorerService.ts Co-authored-by: Charles Gagnon * Fixing promise type * Reverting back to old error logic * Making onsessioncreated async * Removed polling and converted to event based * removing connection variable out of promise * Combining promises * Update src/sql/workbench/services/objectExplorer/browser/treeUpdateUtils.ts Co-authored-by: Charles Gagnon * Fixing error messages and localizing user facing errors * Fixing error message * localizing config * Update src/sql/workbench/services/objectExplorer/browser/objectExplorerService.ts Co-authored-by: Charles Gagnon * Update src/sql/workbench/services/objectExplorer/browser/objectExplorerService.ts Co-authored-by: Charles Gagnon * Update src/sql/workbench/services/objectExplorer/browser/objectExplorerService.ts Co-authored-by: Charles Gagnon * Update src/sql/workbench/services/objectExplorer/browser/objectExplorerService.ts Co-authored-by: Charles Gagnon * Fixing comment --------- Co-authored-by: Charles Gagnon --- .../common/serverGroup.contribution.ts | 3 +- .../browser/asyncServerTreeDataSource.ts | 6 +- .../browser/objectExplorerService.ts | 130 ++++++++++++++---- .../objectExplorer/browser/treeUpdateUtils.ts | 50 +++++-- .../browser/objectExplorerService.test.ts | 38 ++--- 5 files changed, 166 insertions(+), 61 deletions(-) diff --git a/src/sql/workbench/contrib/objectExplorer/common/serverGroup.contribution.ts b/src/sql/workbench/contrib/objectExplorer/common/serverGroup.contribution.ts index 063d1e35f3..8cc403f321 100644 --- a/src/sql/workbench/contrib/objectExplorer/common/serverGroup.contribution.ts +++ b/src/sql/workbench/contrib/objectExplorer/common/serverGroup.contribution.ts @@ -41,9 +41,10 @@ const serverGroupConfig: IConfigurationNode = { } }; +export const NODE_EXPANSION_CONFIG = 'serverTree.nodeExpansionTimeout'; const serverTreeConfig: IConfigurationNode = { 'id': 'serverTree', - 'title': 'Server Tree', + 'title': localize('serverTree.configuration.title', "Server Tree"), 'type': 'object', 'properties': { 'serverTree.useAsyncServerTree': { diff --git a/src/sql/workbench/services/objectExplorer/browser/asyncServerTreeDataSource.ts b/src/sql/workbench/services/objectExplorer/browser/asyncServerTreeDataSource.ts index 51d0977705..3c9630cf92 100644 --- a/src/sql/workbench/services/objectExplorer/browser/asyncServerTreeDataSource.ts +++ b/src/sql/workbench/services/objectExplorer/browser/asyncServerTreeDataSource.ts @@ -13,6 +13,7 @@ import Severity from 'vs/base/common/severity'; import { IErrorMessageService } from 'sql/platform/errorMessage/common/errorMessageService'; import { IAsyncDataSource } from 'vs/base/browser/ui/tree/tree'; import { ServerTreeElement } from 'sql/workbench/services/objectExplorer/browser/asyncServerTree'; +import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; /** * Implements the DataSource(that returns a parent/children of an element) for the server tree @@ -22,6 +23,7 @@ export class AsyncServerTreeDataSource implements IAsyncDataSource { try { if (element instanceof ConnectionProfile) { - return await TreeUpdateUtils.getAsyncConnectionNodeChildren(element, this._connectionManagementService, this._objectExplorerService); + return await TreeUpdateUtils.getAsyncConnectionNodeChildren(element, this._connectionManagementService, this._objectExplorerService, this._configurationService); } else if (element instanceof ConnectionProfileGroup) { - return (element as ConnectionProfileGroup).getChildren(); + return element.getChildren(); } else if (element instanceof TreeNode) { if (element.children) { return element.children; diff --git a/src/sql/workbench/services/objectExplorer/browser/objectExplorerService.ts b/src/sql/workbench/services/objectExplorer/browser/objectExplorerService.ts index e575a38624..31c4fc873d 100644 --- a/src/sql/workbench/services/objectExplorer/browser/objectExplorerService.ts +++ b/src/sql/workbench/services/objectExplorer/browser/objectExplorerService.ts @@ -26,6 +26,7 @@ import { mssqlProviderName } from 'sql/platform/connection/common/constants'; import { ObjectExplorerRequestStatus } from 'sql/workbench/services/objectExplorer/browser/treeSelectionHandler'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { INotificationService } from 'vs/platform/notification/common/notification'; +import { NODE_EXPANSION_CONFIG } from 'sql/workbench/contrib/objectExplorer/common/serverGroup.contribution'; export const SERVICE_ID = 'ObjectExplorerService'; @@ -174,6 +175,10 @@ export class ObjectExplorerService implements IObjectExplorerService { private _onSelectionOrFocusChange: Emitter; private _onNodeExpandedError: Emitter = new Emitter(); + private _onCreateNewSession: Emitter = new Emitter(); + + private _connectionsWaitingForSession: Map = new Map(); + constructor( @IConnectionManagementService private _connectionManagementService: IConnectionManagementService, @IAdsTelemetryService private _telemetryService: IAdsTelemetryService, @@ -264,9 +269,9 @@ export class ObjectExplorerService implements IObjectExplorerService { /** * Gets called when session is created */ - public onSessionCreated(handle: number, session: azdata.ObjectExplorerSession): void { + public async onSessionCreated(handle: number, session: azdata.ObjectExplorerSession): Promise { if (session && session.success) { - this.handleSessionCreated(session).catch((e) => this.logService.error(e)); + await this.handleSessionCreated(session).catch((e) => this.logService.error(e)); } else { let errorMessage = session && session.errorMessage ? session.errorMessage : errSessionCreateFailed; this.logService.error(errorMessage); @@ -276,35 +281,85 @@ export class ObjectExplorerService implements IObjectExplorerService { private async handleSessionCreated(session: azdata.ObjectExplorerSession): Promise { let connection: ConnectionProfile | undefined = undefined; let errorMessage: string | undefined = undefined; - if (this._sessions[session.sessionId!]) { - connection = this._sessions[session.sessionId!].connection; + const sessionId = session.sessionId; - try { - if (session.success && session.rootNode) { - let server = this.toTreeNode(session.rootNode, undefined); - server.connection = connection; - server.session = session; - this._activeObjectExplorerNodes[connection!.id] = server; + + await new Promise((resolve, reject) => { + const cleanup = () => { + if (connection) { + this._connectionsWaitingForSession.delete(connection.id); } - else { - errorMessage = session && session.errorMessage ? session.errorMessage : errSessionCreateFailed; - this.logService.error(errorMessage); - } - // Send on session created about the session to all node providers so they can prepare for node expansion - let nodeProviders = this._nodeProviders[connection!.providerName]; - if (nodeProviders) { - const promises = nodeProviders.map(p => p.handleSessionOpen(session)); - await Promise.all(promises); - } - } catch (error) { - this.logService.warn(`cannot handle the session ${session.sessionId} in all nodeProviders`); - } finally { - this.sendUpdateNodeEvent(connection!, errorMessage); + createNewSessionListener.dispose(); + clearTimeout(timeoutHandle); } + const onTimeout = () => { + if (!this._sessions[sessionId]) { + this.logService.error(`Timed out waiting for session ${sessionId} to be created. + This has probably happened because OE service did not recieve a response for createNewSession from the provider.`); + reject(new Error( + nls.localize('objectExplorerMissingSession', + 'Timed out waiting for session {0} to be created. This has probably happened because OE service did not recieve a response for createNewSession from the provider.', sessionId))); + } else { + this.logService.error(`Timeout waiting for session ${sessionId} to be created for connection "${connection.title}". + This has probably happened because OE service did not recieve a response for createNewSession from the provider for connection."${connection.title}`); + reject(new Error(nls.localize( + 'objectExplorerMissingConnectionForSession', + 'Timeout waiting for session {0} to be created for connection "{1}". This has probably happened because OE service did not recieve a response for createNewSession from the provider for connection "{1}"', sessionId, connection.title + ))); + } + + cleanup(); + } + const timeoutHandle = setTimeout(onTimeout, this.getObjectExplorerTimeout() * 1000); + const createNewSessionListener = this._onCreateNewSession.event((response) => { + checkSessionAndConnection(); + }); + const checkSessionAndConnection = () => { + /** + * Sometimes ads recieves handleSessionCreated from providers before the createNewSession response is recieved. + * We need to wait while the createNewSession response is recieved and the session map contains the session before we can continue. + */ + if (this._sessions[sessionId]) { + connection = this._sessions[sessionId].connection; + /** + * In certain cases, when we try to connect to a previously connected server, we may encounter a situation where the session is present in this._sessions, + * probably becaue the close session request was not completed successfully with the same session id for an older connection. While creating this new session, + * if we recieve the handleSessionCreated event before the createNewSession response is recieved, we will end up using the older connection stored in + * this._sessions[sessionId].connection. To avoid this, we check if the connection id is false in this._connectionsWaitingForSession. If it is not false, + * we know that the createNewSession response has been recieved and we have the correct connection. + */ + if (connection && this._connectionsWaitingForSession.get(connection.id) === false) { + resolve(); + cleanup(); + } + } + } + checkSessionAndConnection(); + }); + + try { + if (session.success && session.rootNode) { + let server = this.toTreeNode(session.rootNode, undefined); + server.connection = connection; + server.session = session; + this._activeObjectExplorerNodes[connection!.id] = server; + } + else { + errorMessage = session && session.errorMessage ? session.errorMessage : errSessionCreateFailed; + this.logService.error(errorMessage); + } + // Send on session created about the session to all node providers so they can prepare for node expansion + let nodeProviders = this._nodeProviders[connection!.providerName]; + if (nodeProviders) { + const promises = nodeProviders.map(p => p.handleSessionOpen(session)); + await Promise.all(promises); + } + } catch (error) { + this.logService.error(`An error occured while handling session ${sessionId} in all nodeProviders.`, error); + } finally { + this.sendUpdateNodeEvent(connection!, errorMessage); } - else { - this.logService.warn(`cannot find session ${session.sessionId}`); - } + } /** @@ -360,7 +415,14 @@ export class ObjectExplorerService implements IObjectExplorerService { public async createNewSession(providerId: string, connection: ConnectionProfile): Promise { const provider = this._providers[providerId]; if (provider) { + // set the connection to wait for session + this._connectionsWaitingForSession.set(connection.id, true); const result = await provider.createNewSession(connection.toConnectionInfo()); + // some providers return a malformed create sessions responses which don't have a session id. We should throw an error in this case + if (!result?.sessionId) { + this.logService.error(`The session ID returned by provider "${providerId}" for connection "${connection.title}" is invalid.`); + throw new Error(nls.localize('objectExplorerSessionIdMissing', 'The session ID returned by provider "{0}" for connection "{1}" is invalid.', providerId, connection.title)); + } if (this._sessions[result.sessionId]) { this.logService.trace(`Overwriting session ${result.sessionId}`); } @@ -368,8 +430,12 @@ export class ObjectExplorerService implements IObjectExplorerService { connection: connection, nodes: {} }; + // once the session is created, set the connection to not wait for session + this._connectionsWaitingForSession.set(connection.id, false); + this._onCreateNewSession.fire(result); return result; } else { + this._connectionsWaitingForSession.delete(connection.id); throw new Error(`Provider doesn't exist. id: ${providerId}`); } } @@ -446,7 +512,6 @@ export class ObjectExplorerService implements IObjectExplorerService { } }); - const expansionTimeoutValueSec = this._configurationService.getValue('serverTree.nodeExpansionTimeout'); const expansionTimeout = setTimeout(() => { /** * If we don't get a response back from all the providers in specified expansion timeout seconds then we assume @@ -458,7 +523,7 @@ export class ObjectExplorerService implements IObjectExplorerService { this._notificationService.error(nls.localize('nodeExpansionTimeout', "Node expansion timed out for node {0} for providers {1}", node.nodePath, missingProviders.map(p => p.providerId).join(', '))); } resolveExpansion(); - }, expansionTimeoutValueSec * 1000); + }, this.getObjectExplorerTimeout() * 1000); self._sessions[session.sessionId!].nodes[node.nodePath].expandEmitter.event((expandResult: NodeExpandInfoWithProviderId) => { if (expandResult && expandResult.providerId) { @@ -932,4 +997,11 @@ export class ObjectExplorerService implements IObjectExplorerService { } return currentNode; } + + /** + * returns object explorer timeout in seconds. + */ + public getObjectExplorerTimeout(): number { + return this._configurationService.getValue(NODE_EXPANSION_CONFIG); + } } diff --git a/src/sql/workbench/services/objectExplorer/browser/treeUpdateUtils.ts b/src/sql/workbench/services/objectExplorer/browser/treeUpdateUtils.ts index d9edbdc5eb..6a684b409a 100644 --- a/src/sql/workbench/services/objectExplorer/browser/treeUpdateUtils.ts +++ b/src/sql/workbench/services/objectExplorer/browser/treeUpdateUtils.ts @@ -15,6 +15,9 @@ 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'; +import * as nls from 'vs/nls'; +import { NODE_EXPANSION_CONFIG } from 'sql/workbench/contrib/objectExplorer/common/serverGroup.contribution'; +import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; export interface IExpandableTree extends ITree { /** @@ -222,8 +225,13 @@ export class TreeUpdateUtils { * @param connectionManagementService Connection management service instance * @param objectExplorerService Object explorer service instance */ - public static async connectAndCreateOeSession(connection: ConnectionProfile, options: IConnectionCompletionOptions, - connectionManagementService: IConnectionManagementService, objectExplorerService: IObjectExplorerService, tree: AsyncServerTree | ITree | undefined, requestStatus?: ObjectExplorerRequestStatus | undefined): Promise { + public static async connectAndCreateOeSession( + connection: ConnectionProfile, + options: IConnectionCompletionOptions, + 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 @@ -264,7 +272,12 @@ export class TreeUpdateUtils { } } - public static async getAsyncConnectionNodeChildren(connection: ConnectionProfile, connectionManagementService: IConnectionManagementService, objectExplorerService: IObjectExplorerService): Promise { + public static async getAsyncConnectionNodeChildren( + connection: ConnectionProfile, + connectionManagementService: IConnectionManagementService, + objectExplorerService: IObjectExplorerService, + configurationService: IConfigurationService + ): Promise { if (connection.isDisconnecting) { return []; } else { @@ -281,14 +294,31 @@ export class TreeUpdateUtils { showFirewallRuleOnError: true, showDashboard: false }; + const expansionTimeoutValueSec = configurationService.getValue(NODE_EXPANSION_CONFIG); // Need to wait for the OE service to update its nodes in order to resolve the children - const nodesUpdatedPromise = new Promise((resolve, reject) => { - objectExplorerService.onUpdateObjectExplorerNodes(e => { - if (e.errorMessage) { - reject(new Error(e.errorMessage)); - } - if (e.connection.id === connection.id) { - resolve(undefined); + const nodesUpdatedPromise = new Promise((resolve, reject) => { + // Clean up timeout and listener + const cleanup = () => { + clearTimeout(nodeUpdateTimer); + nodesUpdatedListener.dispose(); + } + + // If the node update takes too long, reject the promise + const nodeUpdateTimeout = () => { + reject(new Error(nls.localize('objectExplorerTimeout', "Object Explorer expansion timed out for '{0}'", connection.databaseName))); + cleanup(); + } + const nodeUpdateTimer = setTimeout(nodeUpdateTimeout, expansionTimeoutValueSec * 1000); + + + const nodesUpdatedListener = objectExplorerService.onUpdateObjectExplorerNodes(e => { + if (e.connection && e.connection.id === connection.id) { + if (e.errorMessage) { + reject(new Error(e.errorMessage)); + } else { + resolve(); + } + cleanup(); } }); }); diff --git a/src/sql/workbench/services/objectExplorer/test/browser/objectExplorerService.test.ts b/src/sql/workbench/services/objectExplorer/test/browser/objectExplorerService.test.ts index 0afea86120..0d878c7df0 100644 --- a/src/sql/workbench/services/objectExplorer/test/browser/objectExplorerService.test.ts +++ b/src/sql/workbench/services/objectExplorer/test/browser/objectExplorerService.test.ts @@ -327,7 +327,7 @@ suite('SQL Object Explorer Service tests', () => { const session = await objectExplorerService.createNewSession(mssqlProviderName, connection); assert.strictEqual(session !== null || session !== undefined, true); assert.strictEqual(session.sessionId, '1234'); - objectExplorerService.onSessionCreated(1, objectExplorerSession); + await objectExplorerService.onSessionCreated(1, objectExplorerSession); const node = objectExplorerService.getObjectExplorerNode(connection); assert.notStrictEqual(node, undefined); assert.strictEqual(node.session.success, true); @@ -338,7 +338,7 @@ suite('SQL Object Explorer Service tests', () => { assert.strictEqual(session !== null || session !== undefined, true); assert.strictEqual(session.sessionId, failedSessionId); const currentNumberOfSuccessfulSessions = numberOfSuccessfulSessions; - objectExplorerService.onSessionCreated(1, objectExplorerFailedSession); + await objectExplorerService.onSessionCreated(1, objectExplorerFailedSession); const node = objectExplorerService.getObjectExplorerNode(connection); assert.strictEqual(node, undefined); assert.strictEqual(currentNumberOfSuccessfulSessions, numberOfSuccessfulSessions); @@ -354,7 +354,7 @@ suite('SQL Object Explorer Service tests', () => { test('expand node should expand node correctly', async () => { const tablesNode = new TreeNode(NodeType.Folder, '', 'Tables', false, 'testServerName/tables', 'testServerName', '', '', null, null, undefined, undefined); await objectExplorerService.createNewSession(mssqlProviderName, connection); - objectExplorerService.onSessionCreated(1, objectExplorerSession); + await objectExplorerService.onSessionCreated(1, objectExplorerSession); const expandInfo = await objectExplorerService.expandNode(mssqlProviderName, objectExplorerSession, tablesNode); assert.strictEqual(expandInfo !== null || expandInfo !== undefined, true); assert.strictEqual(expandInfo.sessionId, '1234'); @@ -367,7 +367,7 @@ suite('SQL Object Explorer Service tests', () => { test('refresh node should refresh node correctly', async () => { const tablesNode = new TreeNode(NodeType.Folder, '', 'Tables', false, 'testServerName/tables', 'testServerName', '', '', null, null, undefined, undefined); await objectExplorerService.createNewSession(mssqlProviderName, connection); - objectExplorerService.onSessionCreated(1, objectExplorerSession); + await objectExplorerService.onSessionCreated(1, objectExplorerSession); const expandInfo = await objectExplorerService.refreshNode(mssqlProviderName, objectExplorerSession, tablesNode); assert.strictEqual(expandInfo !== null || expandInfo !== undefined, true); assert.strictEqual(expandInfo.sessionId, '1234'); @@ -381,7 +381,7 @@ suite('SQL Object Explorer Service tests', () => { const tablesNode = new TreeNode(NodeType.Folder, '', 'Tables', false, 'testServerName/tables', 'testServerName', '', '', null, null, undefined, undefined); tablesNode.connection = connection; await objectExplorerService.createNewSession(mssqlProviderName, connection); - objectExplorerService.onSessionCreated(1, objectExplorerSession); + await objectExplorerService.onSessionCreated(1, objectExplorerSession); const children = await objectExplorerService.resolveTreeNodeChildren(objectExplorerSession, tablesNode); assert.strictEqual(children !== null || children !== undefined, true); assert.strictEqual(children[0].label, 'dbo.Table1'); @@ -396,7 +396,7 @@ suite('SQL Object Explorer Service tests', () => { const tablesNode = new TreeNode(NodeType.Folder, '', 'Tables', false, 'testServerName/tables', 'testServerName', '', '', null, null, undefined, undefined); tablesNode.connection = connection; await objectExplorerService.createNewSession(mssqlProviderName, connection); - objectExplorerService.onSessionCreated(1, objectExplorerSession); + await objectExplorerService.onSessionCreated(1, objectExplorerSession); const children = await objectExplorerService.refreshTreeNode(objectExplorerSession, tablesNode); assert.strictEqual(children !== null || children !== undefined, true); assert.strictEqual(children[0].label, 'dbo.Table1'); @@ -409,7 +409,7 @@ suite('SQL Object Explorer Service tests', () => { test('update object explorer nodes should get active connection, create session, add to the active OE nodes successfully', async () => { await objectExplorerService.createNewSession(mssqlProviderName, connection); - objectExplorerService.onSessionCreated(1, objectExplorerSession); + await objectExplorerService.onSessionCreated(1, objectExplorerSession); await objectExplorerService.updateObjectExplorerNodes(connection); const treeNode = objectExplorerService.getObjectExplorerNode(connection); assert.strictEqual(treeNode !== null || treeNode !== undefined, true); @@ -421,7 +421,7 @@ suite('SQL Object Explorer Service tests', () => { test('delete object explorerNode nodes should delete session, delete the root node to the active OE node', async () => { await objectExplorerService.createNewSession(mssqlProviderName, connection); - objectExplorerService.onSessionCreated(1, objectExplorerSession); + await objectExplorerService.onSessionCreated(1, objectExplorerSession); await objectExplorerService.updateObjectExplorerNodes(connection); let treeNode = objectExplorerService.getObjectExplorerNode(connection); assert.strictEqual(treeNode !== null && treeNode !== undefined, true); @@ -522,7 +522,7 @@ suite('SQL Object Explorer Service tests', () => { }); objectExplorerService.registerServerTreeView(serverTreeView.object); await objectExplorerService.createNewSession(mssqlProviderName, connection); - objectExplorerService.onSessionCreated(1, objectExplorerSession); + await objectExplorerService.onSessionCreated(1, objectExplorerSession); const childNodes = await objectExplorerService.resolveTreeNodeChildren(objectExplorerSession, objectExplorerService.getObjectExplorerNode(connection)); sqlOEProvider.setup(x => x.expandNode(TypeMoq.It.isAny())).callback(() => { objectExplorerService.onNodeExpanded(tableExpandInfo); @@ -540,7 +540,7 @@ suite('SQL Object Explorer Service tests', () => { }); objectExplorerService.registerServerTreeView(serverTreeView.object); await objectExplorerService.createNewSession(mssqlProviderName, connection); - objectExplorerService.onSessionCreated(1, objectExplorerSession); + await objectExplorerService.onSessionCreated(1, objectExplorerSession); const childNodes = await objectExplorerService.resolveTreeNodeChildren(objectExplorerSession, objectExplorerService.getObjectExplorerNode(connection)); // If I check whether the table is expanded, the answer should be no because only its parent node is expanded const tableNode = childNodes.find(node => node.nodePath === table1NodePath); @@ -562,7 +562,7 @@ suite('SQL Object Explorer Service tests', () => { }); objectExplorerService.registerServerTreeView(serverTreeView.object); await objectExplorerService.createNewSession(mssqlProviderName, connection); - objectExplorerService.onSessionCreated(1, objectExplorerSession); + await objectExplorerService.onSessionCreated(1, objectExplorerSession); const childNodes = await objectExplorerService.resolveTreeNodeChildren(objectExplorerSession, objectExplorerService.getObjectExplorerNode(connection)); sqlOEProvider.setup(x => x.expandNode(TypeMoq.It.isAny())).callback(() => { objectExplorerService.onNodeExpanded(tableExpandInfo); @@ -596,7 +596,7 @@ suite('SQL Object Explorer Service tests', () => { serverTreeView.setup(x => x.reveal(TypeMoq.It.isAny())).returns(() => Promise.resolve()); objectExplorerService.registerServerTreeView(serverTreeView.object); await objectExplorerService.createNewSession(mssqlProviderName, connection); - objectExplorerService.onSessionCreated(1, objectExplorerSession); + await objectExplorerService.onSessionCreated(1, objectExplorerSession); // If I expand the node, then it should get revealed and expanded const tableNode = await objectExplorerService.getTreeNode(connection.id, table1NodePath); await tableNode.setExpandedState(TreeItemCollapsibleState.Expanded); @@ -611,7 +611,7 @@ suite('SQL Object Explorer Service tests', () => { serverTreeView.setup(x => x.setExpandedState(TypeMoq.It.is(treeNode => treeNode === connection), TypeMoq.It.is(state => state === TreeItemCollapsibleState.Collapsed))).returns(() => Promise.resolve()); objectExplorerService.registerServerTreeView(serverTreeView.object); await objectExplorerService.createNewSession(mssqlProviderName, connection); - objectExplorerService.onSessionCreated(1, objectExplorerSession); + await objectExplorerService.onSessionCreated(1, objectExplorerSession); await objectExplorerService.resolveTreeNodeChildren(objectExplorerSession, objectExplorerService.getObjectExplorerNode(connection)); // If I collapse the connection node, then the tree's collapse method should get called const treeNode = await objectExplorerService.getTreeNode(connection.id, undefined); @@ -625,7 +625,7 @@ suite('SQL Object Explorer Service tests', () => { serverTreeView.setup(x => x.reveal(TypeMoq.It.isAny())).returns(() => Promise.resolve()); objectExplorerService.registerServerTreeView(serverTreeView.object); await objectExplorerService.createNewSession(mssqlProviderName, connection); - objectExplorerService.onSessionCreated(1, objectExplorerSession); + await objectExplorerService.onSessionCreated(1, objectExplorerSession); // If I select the table node, then it should be selected and revealed const tableNode = await objectExplorerService.getTreeNode(connection.id, table1NodePath); await tableNode.setSelected(true); @@ -636,7 +636,7 @@ suite('SQL Object Explorer Service tests', () => { test('findTreeNode returns the tree node for the relevant node', async () => { const table1NodePath = objectExplorerExpandInfo.nodes[0].nodePath; await objectExplorerService.createNewSession(mssqlProviderName, connection); - objectExplorerService.onSessionCreated(1, objectExplorerSession); + await objectExplorerService.onSessionCreated(1, objectExplorerSession); const treeNode = await objectExplorerService.getTreeNode(connection.id, table1NodePath); assert.strictEqual(treeNode.nodePath, objectExplorerExpandInfo.nodes[0].nodePath); assert.strictEqual(treeNode.nodeTypeId, objectExplorerExpandInfo.nodes[0].nodeType); @@ -646,7 +646,7 @@ suite('SQL Object Explorer Service tests', () => { test('findTreeNode returns undefined if the requested node does not exist', async () => { const invalidNodePath = objectExplorerSession.rootNode.nodePath + '/invalidNode'; await objectExplorerService.createNewSession(mssqlProviderName, connection); - objectExplorerService.onSessionCreated(1, objectExplorerSession); + await objectExplorerService.onSessionCreated(1, objectExplorerSession); const nodeInfo = await objectExplorerService.getTreeNode(connection.id, invalidNodePath); assert.strictEqual(nodeInfo, undefined); }); @@ -654,7 +654,7 @@ suite('SQL Object Explorer Service tests', () => { test('refreshInView refreshes the node, expands it, and returns the refreshed node', async () => { // Set up the session and tree view await objectExplorerService.createNewSession(mssqlProviderName, connection); - objectExplorerService.onSessionCreated(1, objectExplorerSession); + await objectExplorerService.onSessionCreated(1, objectExplorerSession); serverTreeView.setup(x => x.refreshElement(TypeMoq.It.isAny())).returns(() => Promise.resolve()); objectExplorerService.registerServerTreeView(serverTreeView.object); @@ -673,7 +673,7 @@ suite('SQL Object Explorer Service tests', () => { // Set up the session await objectExplorerService.createNewSession(mssqlProviderName, connection); - objectExplorerService.onSessionCreated(1, objectExplorerSession); + await objectExplorerService.onSessionCreated(1, objectExplorerSession); // Set up the provider to not respond to the second expand request, simulating a request that takes a long time to compconste const nodePath = objectExplorerSession.rootNode.nodePath; @@ -693,7 +693,7 @@ suite('SQL Object Explorer Service tests', () => { test('resolveTreeNodeChildren refreshes a node if it currently has an error', async () => { await objectExplorerService.createNewSession(mssqlProviderName, connection); - objectExplorerService.onSessionCreated(1, objectExplorerSession); + await objectExplorerService.onSessionCreated(1, objectExplorerSession); // If I call resolveTreeNodeChildren once, set an error on the node, and then call it again const tablesNodePath = 'testServerName/tables';