diff --git a/src/sql/parts/objectExplorer/common/objectExplorerService.ts b/src/sql/parts/objectExplorer/common/objectExplorerService.ts index d35fc62565..0dd0847354 100644 --- a/src/sql/parts/objectExplorer/common/objectExplorerService.ts +++ b/src/sql/parts/objectExplorer/common/objectExplorerService.ts @@ -72,6 +72,8 @@ export interface IObjectExplorerService { getActiveConnectionNodes(): TreeNode[]; getTreeNode(connectionId: string, nodePath: string): Thenable; + + refreshNodeInView(connectionId: string, nodePath: string): Thenable; } interface SessionStatus { @@ -476,6 +478,20 @@ export class ObjectExplorerService implements IObjectExplorerService { return Object.values(this._activeObjectExplorerNodes); } + public async refreshNodeInView(connectionId: string, nodePath: string): Promise { + // Get the tree node and call refresh from the provider + let treeNode = await this.getTreeNode(connectionId, nodePath); + await this.refreshTreeNode(treeNode.getSession(), treeNode); + + // Get the new tree node, refresh it in the view, and expand it if needed + treeNode = await this.getTreeNode(connectionId, nodePath); + await this._serverTreeView.refreshElement(treeNode); + if (treeNode.children.length > 0) { + await treeNode.setExpandedState(TreeItemCollapsibleState.Expanded); + } + return treeNode; + } + private async setNodeExpandedState(treeNode: TreeNode, expandedState: TreeItemCollapsibleState): Promise { treeNode = await this.getUpdatedTreeNode(treeNode); let expandNode = this.getTreeItem(treeNode); diff --git a/src/sql/parts/objectExplorer/viewlet/serverTreeView.ts b/src/sql/parts/objectExplorer/viewlet/serverTreeView.ts index a584e64572..cbc77e8ce4 100644 --- a/src/sql/parts/objectExplorer/viewlet/serverTreeView.ts +++ b/src/sql/parts/objectExplorer/viewlet/serverTreeView.ts @@ -138,7 +138,7 @@ export class ServerTreeView { let expandGroups: boolean = self._configurationService.getValue(SERVER_GROUP_CONFIG)[SERVER_GROUP_AUTOEXPAND_CONFIG]; if (expandGroups) { - self._tree.expandAll(ConnectionProfileGroup.getSubgroups(root)); + self._tree.expandAll(ConnectionProfileGroup.getSubgroups(root)); } if (root && !root.hasValidConnections) { @@ -244,6 +244,10 @@ export class ServerTreeView { TreeUpdateUtils.registeredServerUpdate(this._tree, this._connectionManagementService); } + public refreshElement(element: any): Thenable { + return this._tree.refresh(element); + } + /** * Filter connections based on view (recent/active) */ diff --git a/src/sql/sqlops.d.ts b/src/sql/sqlops.d.ts index ee43c51c23..2515752e79 100644 --- a/src/sql/sqlops.d.ts +++ b/src/sql/sqlops.d.ts @@ -185,6 +185,11 @@ declare module 'sqlops' { * Get the parent node. Returns undefined if there is none. */ getParent(): Thenable; + + /** + * Refresh the node, expanding it if it has children + */ + refresh(): Thenable; } } diff --git a/src/sql/workbench/api/node/extHostObjectExplorer.ts b/src/sql/workbench/api/node/extHostObjectExplorer.ts index c5bb5a4fcc..39f28ec67f 100644 --- a/src/sql/workbench/api/node/extHostObjectExplorer.ts +++ b/src/sql/workbench/api/node/extHostObjectExplorer.ts @@ -9,7 +9,7 @@ import { ExtHostObjectExplorerShape, SqlMainContext, MainThreadObjectExplorerSha import * as sqlops from 'sqlops'; import * as vscode from 'vscode'; -export class ExtHostObjectExplorer implements ExtHostObjectExplorerShape { +export class ExtHostObjectExplorer implements ExtHostObjectExplorerShape { private _proxy: MainThreadObjectExplorerShape; @@ -44,7 +44,7 @@ class ExtHostObjectExplorerNode implements sqlops.objectexplorer.ObjectExplorerN public errorMessage: string; constructor(nodeInfo: sqlops.NodeInfo, connectionId: string, private _proxy: MainThreadObjectExplorerShape) { - Object.entries(nodeInfo).forEach(([key, value]) => this[key] = value); + this.getDetailsFromInfo(nodeInfo); this.connectionId = connectionId; } @@ -71,4 +71,12 @@ class ExtHostObjectExplorerNode implements sqlops.objectexplorer.ObjectExplorerN } return this._proxy.$getNode(this.connectionId, this.nodePath.slice(0, parentPathEndIndex)).then(nodeInfo => nodeInfo ? new ExtHostObjectExplorerNode(nodeInfo, this.connectionId, this._proxy) : undefined); } + + refresh(): Thenable { + return this._proxy.$refresh(this.connectionId, this.nodePath).then(nodeInfo => this.getDetailsFromInfo(nodeInfo)); + } + + private getDetailsFromInfo(nodeInfo: sqlops.NodeInfo): void { + Object.entries(nodeInfo).forEach(([key, value]) => this[key] = value); + } } diff --git a/src/sql/workbench/api/node/mainThreadObjectExplorer.ts b/src/sql/workbench/api/node/mainThreadObjectExplorer.ts index 49e76bdde2..56a91ee5bd 100644 --- a/src/sql/workbench/api/node/mainThreadObjectExplorer.ts +++ b/src/sql/workbench/api/node/mainThreadObjectExplorer.ts @@ -15,6 +15,7 @@ import { IEditorService } from 'vs/workbench/services/editor/common/editorServic import * as TaskUtilities from 'sql/workbench/common/taskUtilities'; import { IConnectionProfile } from 'sql/parts/connection/common/interfaces'; import { dispose, IDisposable } from 'vs/base/common/lifecycle'; +import { TreeItemCollapsibleState } from 'sql/parts/objectExplorer/common/treeNode'; @extHostNamedCustomer(SqlMainContext.MainThreadObjectExplorer) export class MainThreadObjectExplorer implements MainThreadObjectExplorerShape { @@ -50,7 +51,7 @@ export class MainThreadObjectExplorer implements MainThreadObjectExplorerShape { public $getActiveConnectionNodes(): Thenable { let connectionNodes = this._objectExplorerService.getActiveConnectionNodes(); return Promise.resolve(connectionNodes.map(node => { - return {connectionId: node.connection.id, nodeInfo: node.toNodeInfo()}; + return { connectionId: node.connection.id, nodeInfo: node.toNodeInfo() }; })); } @@ -73,4 +74,8 @@ export class MainThreadObjectExplorer implements MainThreadObjectExplorerShape { public $findNodes(connectionId: string, type: string, schema: string, name: string, database: string, parentObjectNames: string[]): Thenable { return this._objectExplorerService.findNodes(connectionId, type, schema, name, database, parentObjectNames); } + + public $refresh(connectionId: string, nodePath: string): Thenable { + return this._objectExplorerService.refreshNodeInView(connectionId, nodePath).then(node => node.toNodeInfo()); + } } diff --git a/src/sql/workbench/api/node/sqlExtHost.protocol.ts b/src/sql/workbench/api/node/sqlExtHost.protocol.ts index d80cc1a787..77ce5b268c 100644 --- a/src/sql/workbench/api/node/sqlExtHost.protocol.ts +++ b/src/sql/workbench/api/node/sqlExtHost.protocol.ts @@ -36,7 +36,7 @@ export abstract class ExtHostAccountManagementShape { export abstract class ExtHostConnectionManagementShape { $onConnectionOpened(handleId: string, connection: sqlops.connection.Connection): void { throw ni; } - } +} export abstract class ExtHostDataProtocolShape { @@ -663,6 +663,7 @@ export interface MainThreadObjectExplorerShape extends IDisposable { $getChildren(connectionId: string, nodePath: string): Thenable; $isExpanded(connectionId: string, nodePath: string): Thenable; $findNodes(connectionId: string, type: string, schema: string, name: string, database: string, parentObjectNames: string[]): Thenable; + $refresh(connectionId: string, nodePath: string): Thenable; } export interface ExtHostModelViewDialogShape { diff --git a/src/sqltest/parts/connection/objectExplorerService.test.ts b/src/sqltest/parts/connection/objectExplorerService.test.ts index 50d5b5ab2e..ef1b4345ed 100644 --- a/src/sqltest/parts/connection/objectExplorerService.test.ts +++ b/src/sqltest/parts/connection/objectExplorerService.test.ts @@ -301,7 +301,8 @@ suite('SQL Object Explorer Service tests', () => { reveal: element => Promise.resolve() as Thenable, setSelected: (element, selected, clearOtherSelections) => undefined, isExpanded: element => undefined, - onSelectionOrFocusChange: Event.None + onSelectionOrFocusChange: Event.None, + refreshElement: (element) => Promise.resolve() as Thenable } as ServerTreeView); }); @@ -738,4 +739,22 @@ suite('SQL Object Explorer Service tests', () => { }, err => done(err)); }); }); + + test('refreshInView refreshes the node, expands it, and returns the refreshed node', async () => { + // Set up the session and tree view + await objectExplorerService.createNewSession('MSSQL', connection); + objectExplorerService.onSessionCreated(1, objectExplorerSession); + serverTreeView.setup(x => x.refreshElement(TypeMoq.It.isAny())).returns(() => Promise.resolve()); + objectExplorerService.registerServerTreeView(serverTreeView.object); + + // Refresh the node + let nodePath = objectExplorerSession.rootNode.nodePath; + let refreshedNode = await objectExplorerService.refreshNodeInView(connection.id, nodePath); + + // Verify that it was refreshed, expanded, and the refreshed detailed were returned + sqlOEProvider.verify(x => x.refreshNode(TypeMoq.It.is(refreshNode => refreshNode.nodePath === nodePath)), TypeMoq.Times.once()); + refreshedNode.children.forEach((childNode, index) => { + assert.equal(childNode.nodePath, objectExplorerExpandInfoRefresh.nodes[index].nodePath); + }); + }); }); \ No newline at end of file