diff --git a/extensions/admin-tool-ext-win/src/test/stubs.ts b/extensions/admin-tool-ext-win/src/test/stubs.ts index 7273268fb7..0ae47552a1 100644 --- a/extensions/admin-tool-ext-win/src/test/stubs.ts +++ b/extensions/admin-tool-ext-win/src/test/stubs.ts @@ -16,6 +16,7 @@ export class ExtHostObjectExplorerNodeStub implements azdata.objectexplorer.Obje // Base properties public connectionId: string; public nodePath: string; + public parentNodePath: string; public nodeType: string; public nodeSubType: string; public nodeStatus: string; diff --git a/extensions/azurecore/src/azureResource/messageTreeNode.ts b/extensions/azurecore/src/azureResource/messageTreeNode.ts index da24070dd3..91c4385ce1 100644 --- a/extensions/azurecore/src/azureResource/messageTreeNode.ts +++ b/extensions/azurecore/src/azureResource/messageTreeNode.ts @@ -41,6 +41,7 @@ export class AzureResourceMessageTreeNode extends TreeNode { errorMessage: undefined, metadata: undefined, nodePath: this.generateNodePath(), + parentNodePath: this.parent?.generateNodePath() ?? '', nodeStatus: undefined, nodeType: AzureResourceItemType.message, nodeSubType: undefined, diff --git a/extensions/azurecore/src/azureResource/resourceTreeNode.ts b/extensions/azurecore/src/azureResource/resourceTreeNode.ts index 34270111e8..f6adc5c400 100644 --- a/extensions/azurecore/src/azureResource/resourceTreeNode.ts +++ b/extensions/azurecore/src/azureResource/resourceTreeNode.ts @@ -65,6 +65,7 @@ export class AzureResourceResourceTreeNode extends TreeNode { errorMessage: undefined, metadata: undefined, nodePath: this.generateNodePath(), + parentNodePath: this.parent?.generateNodePath() ?? '', nodeStatus: undefined, nodeType: treeItem.contextValue || '', nodeSubType: undefined, diff --git a/extensions/azurecore/src/azureResource/tree/accountNotSignedInTreeNode.ts b/extensions/azurecore/src/azureResource/tree/accountNotSignedInTreeNode.ts index 902a02be1b..629aef3159 100644 --- a/extensions/azurecore/src/azureResource/tree/accountNotSignedInTreeNode.ts +++ b/extensions/azurecore/src/azureResource/tree/accountNotSignedInTreeNode.ts @@ -34,6 +34,7 @@ export class AzureResourceAccountNotSignedInTreeNode extends TreeNode { errorMessage: undefined, metadata: undefined, nodePath: this.generateNodePath(), + parentNodePath: this.parent?.generateNodePath() ?? '', nodeStatus: undefined, nodeType: AzureResourceItemType.message, nodeSubType: undefined, diff --git a/extensions/azurecore/src/azureResource/tree/accountTreeNode.ts b/extensions/azurecore/src/azureResource/tree/accountTreeNode.ts index 587bda2ba4..251e7b3856 100644 --- a/extensions/azurecore/src/azureResource/tree/accountTreeNode.ts +++ b/extensions/azurecore/src/azureResource/tree/accountTreeNode.ts @@ -120,6 +120,7 @@ export class AzureResourceAccountTreeNode extends AzureResourceContainerTreeNode errorMessage: undefined, metadata: undefined, nodePath: this.generateNodePath(), + parentNodePath: this.parent?.generateNodePath() ?? '', nodeStatus: undefined, nodeType: AzureResourceItemType.account, nodeSubType: undefined, diff --git a/extensions/azurecore/src/azureResource/tree/flatAccountTreeNode.ts b/extensions/azurecore/src/azureResource/tree/flatAccountTreeNode.ts index 5bd75d955b..d25438676c 100644 --- a/extensions/azurecore/src/azureResource/tree/flatAccountTreeNode.ts +++ b/extensions/azurecore/src/azureResource/tree/flatAccountTreeNode.ts @@ -94,6 +94,7 @@ export class FlatAccountTreeNode extends AzureResourceContainerTreeNodeBase { errorMessage: undefined, metadata: undefined, nodePath: this.generateNodePath(), + parentNodePath: this.parent?.generateNodePath() ?? '', nodeStatus: undefined, nodeType: AzureResourceItemType.account, nodeSubType: undefined, diff --git a/extensions/azurecore/src/azureResource/tree/flatTreeProvider.ts b/extensions/azurecore/src/azureResource/tree/flatTreeProvider.ts index 37f9fd83ca..9d28ccdbef 100644 --- a/extensions/azurecore/src/azureResource/tree/flatTreeProvider.ts +++ b/extensions/azurecore/src/azureResource/tree/flatTreeProvider.ts @@ -200,6 +200,7 @@ class AzureResourceResourceTreeNode extends TreeNode { errorMessage: undefined, metadata: undefined, nodePath: this.generateNodePath(), + parentNodePath: this.parent?.generateNodePath() ?? '', nodeStatus: undefined, nodeType: treeItem.contextValue || '', nodeSubType: undefined, diff --git a/extensions/azurecore/src/azureResource/tree/subscriptionTreeNode.ts b/extensions/azurecore/src/azureResource/tree/subscriptionTreeNode.ts index fbbc97cf44..ecc7579d31 100644 --- a/extensions/azurecore/src/azureResource/tree/subscriptionTreeNode.ts +++ b/extensions/azurecore/src/azureResource/tree/subscriptionTreeNode.ts @@ -76,6 +76,7 @@ export class AzureResourceSubscriptionTreeNode extends AzureResourceContainerTre errorMessage: undefined, metadata: undefined, nodePath: this.generateNodePath(), + parentNodePath: this.parent?.generateNodePath() ?? '', nodeStatus: undefined, nodeType: AzureResourceItemType.subscription, nodeSubType: undefined, diff --git a/extensions/azuremonitor/src/objectExplorerNodeProvider/objectExplorerNodeProvider.ts b/extensions/azuremonitor/src/objectExplorerNodeProvider/objectExplorerNodeProvider.ts index c347804dc7..b5867534c0 100644 --- a/extensions/azuremonitor/src/objectExplorerNodeProvider/objectExplorerNodeProvider.ts +++ b/extensions/azuremonitor/src/objectExplorerNodeProvider/objectExplorerNodeProvider.ts @@ -209,6 +209,7 @@ class SqlClusterRootNode extends TreeNode { errorMessage: undefined, metadata: undefined, nodePath: this.generateNodePath()!, + parentNodePath: this.parent?.generateNodePath() ?? '', nodeStatus: undefined, nodeType: 'sqlCluster:root', nodeSubType: undefined, diff --git a/extensions/cms/src/cmsResource/messageTreeNode.ts b/extensions/cms/src/cmsResource/messageTreeNode.ts index 285f4a25d1..5644a48bc0 100644 --- a/extensions/cms/src/cmsResource/messageTreeNode.ts +++ b/extensions/cms/src/cmsResource/messageTreeNode.ts @@ -41,6 +41,7 @@ export class CmsResourceMessageTreeNode extends TreeNode { errorMessage: undefined, metadata: undefined, nodePath: this.generateNodePath(), + parentNodePath: this.parent?.generateNodePath() ?? '', nodeStatus: undefined, nodeType: CmsResourceItemType.cmsMessageNodeContainer, nodeSubType: undefined, diff --git a/extensions/cms/src/cmsResource/tree/cmsResourceEmptyTreeNode.ts b/extensions/cms/src/cmsResource/tree/cmsResourceEmptyTreeNode.ts index 44a790759d..9ac776100d 100644 --- a/extensions/cms/src/cmsResource/tree/cmsResourceEmptyTreeNode.ts +++ b/extensions/cms/src/cmsResource/tree/cmsResourceEmptyTreeNode.ts @@ -34,6 +34,7 @@ export class CmsResourceEmptyTreeNode extends TreeNode { errorMessage: undefined, metadata: undefined, nodePath: this.generateNodePath(), + parentNodePath: this.parent?.generateNodePath() ?? '', nodeStatus: undefined, nodeType: CmsResourceItemType.cmsEmptyNodeContainer, iconType: CmsResourceItemType.cmsEmptyNodeContainer, diff --git a/extensions/cms/src/cmsResource/tree/cmsResourceTreeNode.ts b/extensions/cms/src/cmsResource/tree/cmsResourceTreeNode.ts index 5d11c328ba..2d6dd66fa9 100644 --- a/extensions/cms/src/cmsResource/tree/cmsResourceTreeNode.ts +++ b/extensions/cms/src/cmsResource/tree/cmsResourceTreeNode.ts @@ -108,6 +108,7 @@ export class CmsResourceTreeNode extends CmsResourceTreeNodeBase { errorMessage: undefined, metadata: undefined, nodePath: this.generateNodePath(), + parentNodePath: this.parent?.generateNodePath() ?? '', nodeStatus: undefined, nodeType: CmsResourceItemType.cmsNodeContainer, nodeSubType: undefined, diff --git a/extensions/cms/src/cmsResource/tree/registeredServerTreeNode.ts b/extensions/cms/src/cmsResource/tree/registeredServerTreeNode.ts index 99090fc533..b7c1866c96 100644 --- a/extensions/cms/src/cmsResource/tree/registeredServerTreeNode.ts +++ b/extensions/cms/src/cmsResource/tree/registeredServerTreeNode.ts @@ -71,6 +71,7 @@ export class RegisteredServerTreeNode extends CmsResourceTreeNodeBase { errorMessage: undefined, metadata: undefined, nodePath: this.generateNodePath(), + parentNodePath: this.parent?.generateNodePath() ?? '', nodeStatus: undefined, nodeType: CmsResourceItemType.registeredServer, nodeSubType: undefined diff --git a/extensions/cms/src/cmsResource/tree/serverGroupTreeNode.ts b/extensions/cms/src/cmsResource/tree/serverGroupTreeNode.ts index 4fc7d419cf..d0b9f586e6 100644 --- a/extensions/cms/src/cmsResource/tree/serverGroupTreeNode.ts +++ b/extensions/cms/src/cmsResource/tree/serverGroupTreeNode.ts @@ -95,6 +95,7 @@ export class ServerGroupTreeNode extends CmsResourceTreeNodeBase { errorMessage: undefined, metadata: undefined, nodePath: this.generateNodePath(), + parentNodePath: this.parent?.generateNodePath() ?? '', nodeStatus: undefined, nodeType: CmsResourceItemType.serverGroup, nodeSubType: undefined diff --git a/extensions/datavirtualization/src/hdfsProvider.ts b/extensions/datavirtualization/src/hdfsProvider.ts index d9ab0839fe..caf5b2b2b8 100644 --- a/extensions/datavirtualization/src/hdfsProvider.ts +++ b/extensions/datavirtualization/src/hdfsProvider.ts @@ -79,6 +79,7 @@ export class FileNode extends HdfsFileSourceNode implements IFileNode { errorMessage: undefined, metadata: undefined, nodePath: this.generateNodePath(), + parentNodePath: this.parent?.generateNodePath() ?? '', nodeStatus: undefined, nodeType: Constants.HdfsItems.File, nodeSubType: this.getSubType(), diff --git a/extensions/kusto/src/objectExplorerNodeProvider/objectExplorerNodeProvider.ts b/extensions/kusto/src/objectExplorerNodeProvider/objectExplorerNodeProvider.ts index bf5f7e7bcd..e1cb8e0dc6 100644 --- a/extensions/kusto/src/objectExplorerNodeProvider/objectExplorerNodeProvider.ts +++ b/extensions/kusto/src/objectExplorerNodeProvider/objectExplorerNodeProvider.ts @@ -210,6 +210,7 @@ class SqlClusterRootNode extends TreeNode { errorMessage: undefined, metadata: undefined, nodePath: this.generateNodePath()!, + parentNodePath: this.parent?.generateNodePath() ?? '', nodeStatus: undefined, nodeType: 'sqlCluster:root', nodeSubType: undefined, diff --git a/extensions/mssql/config.json b/extensions/mssql/config.json index 9f2df359fe..0012378051 100644 --- a/extensions/mssql/config.json +++ b/extensions/mssql/config.json @@ -1,6 +1,6 @@ { "downloadUrl": "https://github.com/Microsoft/sqltoolsservice/releases/download/{#version#}/microsoft.sqltools.servicelayer-{#fileName#}", - "version": "4.6.0.12", + "version": "4.6.0.13", "downloadFileNames": { "Windows_86": "win-x86-net7.0.zip", "Windows_64": "win-x64-net7.0.zip", diff --git a/src/sql/azdata.d.ts b/src/sql/azdata.d.ts index 21adfbff9b..7500d8a5c5 100644 --- a/src/sql/azdata.d.ts +++ b/src/sql/azdata.d.ts @@ -308,7 +308,7 @@ declare module 'azdata' { /** * Get the parent node. Returns undefined if there is none. */ - getParent(): Thenable; + getParent(): Thenable; /** * Refresh the node, expanding it if it has children diff --git a/src/sql/azdata.proposed.d.ts b/src/sql/azdata.proposed.d.ts index 32b5bbf0a8..5dde4e7f69 100644 --- a/src/sql/azdata.proposed.d.ts +++ b/src/sql/azdata.proposed.d.ts @@ -1880,6 +1880,10 @@ declare module 'azdata' { * under the database, the nodeType is Folder, the objectType is be Tables. */ objectType?: string; + /* + * The path of the parent node. + */ + parentNodePath: string; } export namespace window { diff --git a/src/sql/workbench/api/common/extHostObjectExplorer.ts b/src/sql/workbench/api/common/extHostObjectExplorer.ts index b1d8faeff8..ac0d19baa3 100644 --- a/src/sql/workbench/api/common/extHostObjectExplorer.ts +++ b/src/sql/workbench/api/common/extHostObjectExplorer.ts @@ -66,6 +66,7 @@ export class ExtHostObjectExplorer implements ExtHostObjectExplorerShape { export class ExtHostObjectExplorerNode implements azdata.objectexplorer.ObjectExplorerNode { public connectionId: string; public nodePath: string; + public parentNodePath: string; public nodeType: string; public nodeSubType: string; public nodeStatus: string; @@ -95,21 +96,30 @@ export class ExtHostObjectExplorerNode implements azdata.objectexplorer.ObjectEx return this._proxy.$getChildren(this.connectionId, this.nodePath).then(children => children.map(nodeInfo => new ExtHostObjectExplorerNode(nodeInfo, this.connectionId, this._proxy))); } - getParent(): Thenable { - // Object nodes have a name like . in the nodePath - we can't use label because - // that may have additional display information appended to it. Items without metadata are nodes - // such as folders that don't correspond to actual objects and so just use the label - let nodePathName = this.metadata ? - `${this.metadata.schema ? this.metadata.schema + '.' : ''}${this.metadata.name}` : - this.label; + getParent(): Thenable { + let parentPath; + // parentNodePath property is introduced after we discovered the flaw of the following code. + // To maintain backward compatibility with other providers, we need to keep the following code. + if (this.parentNodePath === undefined) { + // Object nodes have a name like . in the nodePath - we can't use label because + // that may have additional display information appended to it. Items without metadata are nodes + // such as folders that don't correspond to actual objects and so just use the label + let nodePathName = this.metadata ? + `${this.metadata.schema ? this.metadata.schema + '.' : ''}${this.metadata.name}` : + this.label; - // -1 to remove the / as well - let parentPathEndIndex: number = this.nodePath.lastIndexOf(nodePathName) - 1; - if (parentPathEndIndex < 0) { + // -1 to remove the / as well + let parentPathEndIndex: number = this.nodePath.lastIndexOf(nodePathName) - 1; + parentPath = this.nodePath.slice(0, parentPathEndIndex) + } else { + parentPath = this.parentNodePath; + } + + if (!parentPath) { // At root node return Promise.resolve(undefined); } - return this._proxy.$getNode(this.connectionId, this.nodePath.slice(0, parentPathEndIndex)).then( + return this._proxy.$getNode(this.connectionId, this.parentNodePath).then( nodeInfo => nodeInfo ? new ExtHostObjectExplorerNode(nodeInfo, this.connectionId, this._proxy) : undefined); } @@ -119,6 +129,7 @@ export class ExtHostObjectExplorerNode implements azdata.objectexplorer.ObjectEx private getDetailsFromInfo(nodeInfo: azdata.NodeInfo): void { this.nodePath = nodeInfo.nodePath; + this.parentNodePath = nodeInfo.parentNodePath; this.nodeType = nodeInfo.nodeType; this.nodeSubType = nodeInfo.nodeSubType; this.nodeStatus = nodeInfo.nodeStatus; diff --git a/src/sql/workbench/contrib/objectExplorer/test/browser/connectionTreeActions.test.ts b/src/sql/workbench/contrib/objectExplorer/test/browser/connectionTreeActions.test.ts index cbcf58588a..56917ff6a2 100644 --- a/src/sql/workbench/contrib/objectExplorer/test/browser/connectionTreeActions.test.ts +++ b/src/sql/workbench/contrib/objectExplorer/test/browser/connectionTreeActions.test.ts @@ -109,8 +109,8 @@ suite('SQL Connection Tree Action tests', () => { let objectExplorerService = TypeMoq.Mock.ofType(ObjectExplorerService, TypeMoq.MockBehavior.Strict, connectionManagementService); objectExplorerService.callBase = true; objectExplorerService.setup(x => x.getTreeNode(TypeMoq.It.isAny(), TypeMoq.It.isAny())).returns(() => Promise.resolve(getTreeNodeReturnVal)); - objectExplorerService.setup(x => x.getObjectExplorerNode(TypeMoq.It.isAny())).returns(() => new TreeNode('', '', '', false, '', '', '', undefined, undefined, undefined, undefined)); - objectExplorerService.setup(x => x.getObjectExplorerNode(undefined)).returns(() => new TreeNode('', '', '', false, '', '', '', undefined, undefined, undefined, undefined)); + objectExplorerService.setup(x => x.getObjectExplorerNode(TypeMoq.It.isAny())).returns(() => new TreeNode('', '', '', false, '', '', '', '', undefined, undefined, undefined, undefined)); + objectExplorerService.setup(x => x.getObjectExplorerNode(undefined)).returns(() => new TreeNode('', '', '', false, '', '', '', '', undefined, undefined, undefined, undefined)); objectExplorerService.setup(x => x.onUpdateObjectExplorerNodes).returns(() => new Emitter().event); objectExplorerService.setup(x => x.onUpdateObjectExplorerNodes).returns(() => new Emitter().event); @@ -223,7 +223,7 @@ suite('SQL Connection Tree Action tests', () => { saveProfile: true, id: 'testId' }); - let treeNode = new TreeNode(NodeType.Database, '', 'db node', false, '', '', '', undefined, undefined, undefined, undefined); + let treeNode = new TreeNode(NodeType.Database, '', 'db node', false, '', '', '', '', undefined, undefined, undefined, undefined); treeNode.connection = connection; let connectionManagementService = createConnectionManagementService(isConnectedReturnValue, connection); let editorService = createEditorService(); @@ -471,7 +471,8 @@ suite('SQL Connection Tree Action tests', () => { success: true, sessionId: '1234', rootNode: { - nodePath: 'testServerName\tables', + nodePath: 'testServerName/tables', + parentNodePath: 'testServerName', nodeType: NodeType.Folder, objectType: '', label: 'Tables', @@ -484,11 +485,11 @@ suite('SQL Connection Tree Action tests', () => { errorMessage: '' }; - let tablesNode = new TreeNode(NodeType.Folder, '', 'Tables', false, 'testServerName\Db1\tables', '', '', null, null, undefined, undefined); + let tablesNode = new TreeNode(NodeType.Folder, '', 'Tables', false, 'testServerName/Db1/tables', 'testServerName/Db1', '', '', null, null, undefined, undefined); tablesNode.connection = connection; tablesNode.session = objectExplorerSession; - let table1Node = new TreeNode(NodeType.Table, '', 'dbo.Table1', false, 'testServerName\tables\dbo.Table1', '', '', tablesNode, null, undefined, undefined); - let table2Node = new TreeNode(NodeType.Table, '', 'dbo.Table1', false, 'testServerName\tables\dbo.Table1', '', '', tablesNode, null, undefined, undefined); + let table1Node = new TreeNode(NodeType.Table, '', 'dbo.Table1', false, 'testServerName/tables/dbo.Table1', 'testServerName/tables', '', '', tablesNode, null, undefined, undefined); + let table2Node = new TreeNode(NodeType.Table, '', 'dbo.Table1', false, 'testServerName/tables/dbo.Table1', 'testServerName/tables', '', '', tablesNode, null, undefined, undefined); tablesNode.children = [table1Node, table2Node]; let objectExplorerService = TypeMoq.Mock.ofType(ObjectExplorerService, TypeMoq.MockBehavior.Loose, connectionManagementService.object); objectExplorerService.callBase = true; @@ -558,7 +559,8 @@ suite('SQL Connection Tree Action tests', () => { success: true, sessionId: '1234', rootNode: { - nodePath: 'testServerName\tables', + nodePath: 'testServerName/tables', + parentNodePath: 'testServerName', nodeType: NodeType.Folder, objectType: '', label: 'Tables', @@ -571,11 +573,11 @@ suite('SQL Connection Tree Action tests', () => { errorMessage: '' }; - let tablesNode = new TreeNode(NodeType.Folder, '', 'Tables', false, 'testServerName\Db1\tables', '', '', null, null, undefined, undefined); + let tablesNode = new TreeNode(NodeType.Folder, '', 'Tables', false, 'testServerName/Db1/tables', 'testServerName/Db1', '', '', null, null, undefined, undefined); tablesNode.connection = connection; tablesNode.session = objectExplorerSession; - let table1Node = new TreeNode(NodeType.Table, '', 'dbo.Table1', false, 'testServerName\tables\dbo.Table1', '', '', tablesNode, null, undefined, undefined); - let table2Node = new TreeNode(NodeType.Table, '', 'dbo.Table1', false, 'testServerName\tables\dbo.Table1', '', '', tablesNode, null, undefined, undefined); + let table1Node = new TreeNode(NodeType.Table, '', 'dbo.Table1', false, 'testServerName/tables/dbo.Table1', 'testServerName/tables', '', '', tablesNode, null, undefined, undefined); + let table2Node = new TreeNode(NodeType.Table, '', 'dbo.Table1', false, 'testServerName/tables/dbo.Table1', 'testServerName/tables', '', '', tablesNode, null, undefined, undefined); tablesNode.children = [table1Node, table2Node]; let objectExplorerService = TypeMoq.Mock.ofType(ObjectExplorerService, TypeMoq.MockBehavior.Loose, connectionManagementService.object); objectExplorerService.callBase = true; @@ -646,7 +648,8 @@ suite('SQL Connection Tree Action tests', () => { success: true, sessionId: '1234', rootNode: { - nodePath: 'testServerName\tables', + nodePath: 'testServerName/tables', + parentNodePath: 'testServerName', nodeType: NodeType.Folder, objectType: '', label: 'Tables', @@ -659,11 +662,11 @@ suite('SQL Connection Tree Action tests', () => { errorMessage: '' }; - let tablesNode = new TreeNode(NodeType.Folder, '', 'Tables', false, 'testServerName\Db1\tables', '', '', null, null, undefined, undefined); + let tablesNode = new TreeNode(NodeType.Folder, '', 'Tables', false, 'testServerName/Db1/tables', 'testServerName/Db1', '', '', null, null, undefined, undefined); tablesNode.connection = connection; tablesNode.session = objectExplorerSession; - let table1Node = new TreeNode(NodeType.Table, '', 'dbo.Table1', false, 'testServerName\tables\dbo.Table1', '', '', tablesNode, null, undefined, undefined); - let table2Node = new TreeNode(NodeType.Table, '', 'dbo.Table1', false, 'testServerName\tables\dbo.Table1', '', '', tablesNode, null, undefined, undefined); + let table1Node = new TreeNode(NodeType.Table, '', 'dbo.Table1', false, 'testServerName/tables/dbo.Table1', 'testServerName/tables', '', '', tablesNode, null, undefined, undefined); + let table2Node = new TreeNode(NodeType.Table, '', 'dbo.Table1', false, 'testServerName/tables/dbo.Table1', 'testServerName/tables', '', '', tablesNode, null, undefined, undefined); tablesNode.children = [table1Node, table2Node]; let objectExplorerService = TypeMoq.Mock.ofType(ObjectExplorerService, TypeMoq.MockBehavior.Loose, connectionManagementService.object); objectExplorerService.callBase = true; @@ -743,7 +746,8 @@ suite('SQL Connection Tree Action tests', () => { success: true, sessionId: '1234', rootNode: { - nodePath: 'testServerName\tables', + nodePath: 'testServerName/tables', + parentNodePath: 'testServerName', nodeType: NodeType.Folder, objectType: '', label: 'Tables', @@ -756,11 +760,11 @@ suite('SQL Connection Tree Action tests', () => { errorMessage: '' }; - let tablesNode = new TreeNode(NodeType.Folder, '', 'Tables', false, 'testServerName\Db1\tables', '', '', null, null, undefined, undefined); + let tablesNode = new TreeNode(NodeType.Folder, '', 'Tables', false, 'testServerName/Db1/tables', 'testServerName/Db1', '', '', null, null, undefined, undefined); tablesNode.connection = connection; tablesNode.session = objectExplorerSession; - let table1Node = new TreeNode(NodeType.Table, '', 'dbo.Table1', false, 'testServerName\tables\dbo.Table1', '', '', tablesNode, null, undefined, undefined); - let table2Node = new TreeNode(NodeType.Table, '', 'dbo.Table1', false, 'testServerName\tables\dbo.Table1', '', '', tablesNode, null, undefined, undefined); + let table1Node = new TreeNode(NodeType.Table, '', 'dbo.Table1', false, 'testServerName/tables/dbo.Table1', 'testServerName/tables', '', '', tablesNode, null, undefined, undefined); + let table2Node = new TreeNode(NodeType.Table, '', 'dbo.Table1', false, 'testServerName/tables/dbo.Table1', 'testServerName/tables', '', '', tablesNode, null, undefined, undefined); tablesNode.children = [table1Node, table2Node]; let objectExplorerService = TypeMoq.Mock.ofType(ObjectExplorerService, TypeMoq.MockBehavior.Loose, connectionManagementService.object); objectExplorerService.callBase = true; diff --git a/src/sql/workbench/contrib/scripting/test/browser/scriptingActions.test.ts b/src/sql/workbench/contrib/scripting/test/browser/scriptingActions.test.ts index d81500f042..e6bc9f6ac1 100644 --- a/src/sql/workbench/contrib/scripting/test/browser/scriptingActions.test.ts +++ b/src/sql/workbench/contrib/scripting/test/browser/scriptingActions.test.ts @@ -42,6 +42,7 @@ const connection: azdata.IConnectionProfile = { const nodeInfo: azdata.NodeInfo = { nodePath: 'MyServer', + parentNodePath: '', objectType: '', nodeStatus: '', nodeSubType: '', @@ -52,7 +53,7 @@ const nodeInfo: azdata.NodeInfo = { errorMessage: '' }; -const treeNode = new TreeNode(NodeType.Database, '', 'db node', false, '', '', '', undefined, undefined, undefined, undefined); +const treeNode = new TreeNode(NodeType.Database, '', 'db node', false, '', '', '', '', undefined, undefined, undefined, undefined); const oeActionArgs: ObjectExplorerActionsContext = { connectionProfile: connection, isConnectionNode: false, nodeInfo: nodeInfo }; let instantiationService: IInstantiationService; diff --git a/src/sql/workbench/services/objectExplorer/browser/objectExplorerService.ts b/src/sql/workbench/services/objectExplorer/browser/objectExplorerService.ts index 3c8b508f73..e575a38624 100644 --- a/src/sql/workbench/services/objectExplorer/browser/objectExplorerService.ts +++ b/src/sql/workbench/services/objectExplorer/browser/objectExplorerService.ts @@ -522,6 +522,7 @@ export class ObjectExplorerService implements IObjectExplorerService { let allNodes: azdata.NodeInfo[] = []; let errorNode: azdata.NodeInfo = { nodePath: nodePath, + parentNodePath: '', objectType: 'error', label: 'Error', errorMessage: '', @@ -690,7 +691,7 @@ export class ObjectExplorerService implements IObjectExplorerService { } } - let node = new TreeNode(nodeInfo.nodeType, nodeInfo.objectType, nodeInfo.label, isLeaf, nodeInfo.nodePath, + let node = new TreeNode(nodeInfo.nodeType, nodeInfo.objectType, nodeInfo.label, isLeaf, nodeInfo.nodePath, nodeInfo.parentNodePath, nodeInfo.nodeSubType!, nodeInfo.nodeStatus, parent, nodeInfo.metadata, nodeInfo.iconType, nodeInfo.icon, { getChildren: (treeNode?: TreeNode) => this.getChildren(treeNode), isExpanded: treeNode => this.isExpanded(treeNode), diff --git a/src/sql/workbench/services/objectExplorer/browser/objectExplorerViewTreeShim.ts b/src/sql/workbench/services/objectExplorer/browser/objectExplorerViewTreeShim.ts index 0120a3d8b6..139422c669 100644 --- a/src/sql/workbench/services/objectExplorer/browser/objectExplorerViewTreeShim.ts +++ b/src/sql/workbench/services/objectExplorer/browser/objectExplorerViewTreeShim.ts @@ -125,7 +125,7 @@ export class OEShimService extends Disposable implements IOEShimService { node.sessionId = await this.createSession(viewId, node.childProvider!, node); } const requestHandle = this.nodeHandleMap.get(generateNodeMapKey(viewId, node)) || node.handle; - const treeNode = new TreeNode(undefined!, undefined!, undefined!, undefined!, requestHandle, undefined!); // hack since this entire system is a hack anyways + const treeNode = new TreeNode(undefined!, undefined!, undefined!, undefined!, requestHandle, undefined!, undefined!); // hack since this entire system is a hack anyways treeNode.connection = new ConnectionProfile(this.capabilities, node.payload); const childrenNodes = await this.oe.refreshTreeNode({ success: true, @@ -168,6 +168,7 @@ export class OEShimService extends Disposable implements IOEShimService { } const nodeInfo: azdata.NodeInfo = { nodePath: nodePath, + parentNodePath: node.parentNodePath, nodeType: node.nodeTypeId, objectType: node.objectType, nodeSubType: node.nodeSubType, diff --git a/src/sql/workbench/services/objectExplorer/browser/serverTreeActionProvider.ts b/src/sql/workbench/services/objectExplorer/browser/serverTreeActionProvider.ts index 869ff1919d..5bf6895748 100644 --- a/src/sql/workbench/services/objectExplorer/browser/serverTreeActionProvider.ts +++ b/src/sql/workbench/services/objectExplorer/browser/serverTreeActionProvider.ts @@ -69,7 +69,7 @@ export class ServerTreeActionProvider { * Return actions for connection elements */ private getConnectionActions(tree: AsyncServerTree | ITree, profile: ConnectionProfile): IAction[] { - let node = new TreeNode(NodeType.Server, NodeType.Server, '', false, '', '', '', undefined, undefined, undefined, undefined); + let node = new TreeNode(NodeType.Server, NodeType.Server, '', false, '', '', '', '', undefined, undefined, undefined, undefined); // Only update password and not access tokens to avoid login prompts when opening context menu. this._connectionManagementService.addSavedPassword(profile, true); node.connection = profile; diff --git a/src/sql/workbench/services/objectExplorer/common/treeNode.ts b/src/sql/workbench/services/objectExplorer/common/treeNode.ts index abc86e8452..fe5e8f915a 100644 --- a/src/sql/workbench/services/objectExplorer/common/treeNode.ts +++ b/src/sql/workbench/services/objectExplorer/common/treeNode.ts @@ -73,6 +73,11 @@ export class TreeNode { */ public nodePath: string; + /** + * Parent node path + */ + public parentNodePath: string; + /** * Node sub type */ @@ -99,7 +104,7 @@ export class TreeNode { public icon?: IconPath | SqlThemeIcon; - constructor(nodeTypeId: string, objectType: string, label: string, isAlwaysLeaf: boolean, nodePath: string, + constructor(nodeTypeId: string, objectType: string, label: string, isAlwaysLeaf: boolean, nodePath: string, parentNodePath: string, nodeSubType: string, nodeStatus?: string, parent?: TreeNode, metadata?: azdata.ObjectMetadata, iconType?: string | SqlThemeIcon, icon?: IconPath | SqlThemeIcon, @@ -109,6 +114,7 @@ export class TreeNode { this.label = label; this.isAlwaysLeaf = isAlwaysLeaf; this.nodePath = nodePath; + this.parentNodePath = parentNodePath; this.parent = parent; this.metadata = metadata; this.iconType = iconType; @@ -158,6 +164,7 @@ export class TreeNode { public toNodeInfo(): azdata.NodeInfo { return { nodePath: this.nodePath, + parentNodePath: this.parentNodePath, nodeType: this.nodeTypeId, nodeSubType: this.nodeSubType, nodeStatus: this.nodeStatus, 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 e47ff076ef..0afea86120 100644 --- a/src/sql/workbench/services/objectExplorer/test/browser/objectExplorerService.test.ts +++ b/src/sql/workbench/services/objectExplorer/test/browser/objectExplorerService.test.ts @@ -46,6 +46,7 @@ suite('SQL Object Explorer Service tests', () => { const NodeInfoTable1 = { nodePath: 'testServerName/tables/dbo.Table1', + parentNodePath: 'testServerName/tables', nodeType: NodeType.Table, objectType: '', label: 'dbo.Table1', @@ -57,6 +58,7 @@ suite('SQL Object Explorer Service tests', () => { }; const NodeInfoTable2 = { nodePath: 'testServerName/tables/dbo.Table2', + parentNodePath: 'testServerName/tables', nodeType: NodeType.Table, objectType: '', label: 'dbo.Table2', @@ -69,6 +71,7 @@ suite('SQL Object Explorer Service tests', () => { const NodeInfoTable3 = { nodePath: 'testServerName/tables/dbo.Table3', + parentNodePath: 'testServerName/tables', nodeType: NodeType.Table, objectType: '', label: 'dbo.Table3', @@ -84,6 +87,7 @@ suite('SQL Object Explorer Service tests', () => { sessionId: sessionId, rootNode: { nodePath: 'testServerName/tables', + parentNodePath: 'testServerName', nodeType: NodeType.Folder, objectType: '', label: 'Tables', @@ -348,7 +352,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', '', '', null, null, undefined, undefined); + const tablesNode = new TreeNode(NodeType.Folder, '', 'Tables', false, 'testServerName/tables', 'testServerName', '', '', null, null, undefined, undefined); await objectExplorerService.createNewSession(mssqlProviderName, connection); objectExplorerService.onSessionCreated(1, objectExplorerSession); const expandInfo = await objectExplorerService.expandNode(mssqlProviderName, objectExplorerSession, tablesNode); @@ -361,7 +365,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', '', '', null, null, undefined, undefined); + const tablesNode = new TreeNode(NodeType.Folder, '', 'Tables', false, 'testServerName/tables', 'testServerName', '', '', null, null, undefined, undefined); await objectExplorerService.createNewSession(mssqlProviderName, connection); objectExplorerService.onSessionCreated(1, objectExplorerSession); const expandInfo = await objectExplorerService.refreshNode(mssqlProviderName, objectExplorerSession, tablesNode); @@ -374,7 +378,7 @@ suite('SQL Object Explorer Service tests', () => { }); test('expand tree node should get correct children', async () => { - const tablesNode = new TreeNode(NodeType.Folder, '', 'Tables', false, 'testServerName/tables', '', '', null, null, undefined, undefined); + 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); @@ -389,7 +393,7 @@ suite('SQL Object Explorer Service tests', () => { }); test('refresh tree node should children correctly', async () => { - const tablesNode = new TreeNode(NodeType.Folder, '', 'Tables', false, 'testServerName/tables', '', '', null, null, undefined, undefined); + 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); @@ -436,13 +440,13 @@ suite('SQL Object Explorer Service tests', () => { parentName: undefined, parentTypeName: undefined }; - const databaseNode = new TreeNode(NodeType.Database, '', 'Db1', false, 'testServerName\\Db1', '', '', undefined, databaseMetaData, undefined, undefined); + const databaseNode = new TreeNode(NodeType.Database, '', 'Db1', false, 'testServerName/Db1', 'testServerName', '', '', undefined, databaseMetaData, undefined, undefined); databaseNode.connection = connection; databaseNode.session = objectExplorerSession; - const tablesNode = new TreeNode(NodeType.Folder, '', 'Tables', false, 'testServerName\\Db1\\tables', '', '', databaseNode, undefined, undefined, undefined); + const tablesNode = new TreeNode(NodeType.Folder, '', 'Tables', false, 'testServerName/Db1/tables', 'testServerName/Db1', '', '', databaseNode, undefined, undefined, undefined); databaseNode.children = [tablesNode]; - const table1Node = new TreeNode(NodeType.Table, '', 'dbo.Table1', false, 'testServerName\\Db1\\tables\\dbo.Table1', '', '', tablesNode, undefined, undefined, undefined); - const table2Node = new TreeNode(NodeType.Table, '', 'dbo.Table2', false, 'testServerName\\Db1\\tables\\dbo.Table2', '', '', tablesNode, undefined, undefined, undefined); + const table1Node = new TreeNode(NodeType.Table, '', 'dbo.Table1', false, 'testServerName/Db1/tables/dbo.Table1', 'testServerName/Db1/tables', '', '', tablesNode, undefined, undefined, undefined); + const table2Node = new TreeNode(NodeType.Table, '', 'dbo.Table2', false, 'testServerName/Db1/tables/dbo.Table2', 'testServerName/Db1/tables', '', '', tablesNode, undefined, undefined, undefined); tablesNode.children = [table1Node, table2Node]; assert.strictEqual(table1Node.getSession(), objectExplorerSession); assert.strictEqual(table1Node.getConnectionProfile(), connection); @@ -461,7 +465,7 @@ suite('SQL Object Explorer Service tests', () => { test('getSelectedProfileAndDatabase returns the profile but no database if children of a server are selected', () => { const serverTreeView = TypeMoq.Mock.ofInstance({ getSelection: () => undefined, onSelectionOrFocusChange: Event.None } as IServerTreeView); - const databaseNode = new TreeNode(NodeType.Folder, '', 'Folder1', false, 'testServerName\\Folder1', '', '', undefined, undefined, undefined, undefined); + const databaseNode = new TreeNode(NodeType.Folder, '', 'Folder1', false, 'testServerName/Folder1', 'testServerName', '', '', undefined, undefined, undefined, undefined); databaseNode.connection = connection; serverTreeView.setup(x => x.getSelection()).returns(() => [databaseNode]); objectExplorerService.registerServerTreeView(serverTreeView.object); @@ -483,8 +487,8 @@ suite('SQL Object Explorer Service tests', () => { parentTypeName: undefined }; const databaseName = 'Db1'; - const databaseNode = new TreeNode(NodeType.Database, '', databaseName, false, 'testServerName\\Db1', '', '', undefined, databaseMetadata, undefined, undefined); - const tablesNode = new TreeNode(NodeType.Folder, '', 'Tables', false, 'testServerName\\Db1\\tables', '', '', databaseNode, undefined, undefined, undefined); + const databaseNode = new TreeNode(NodeType.Database, '', databaseName, false, 'testServerName/Db1', 'testServerName', '', '', undefined, databaseMetadata, undefined, undefined); + const tablesNode = new TreeNode(NodeType.Folder, '', 'Tables', false, 'testServerName/Db1/tables', 'testServerName/Db1', '', '', databaseNode, undefined, undefined, undefined); databaseNode.connection = connection; databaseNode.children = [tablesNode]; serverTreeView.setup(x => x.getSelection()).returns(() => [tablesNode]); @@ -676,7 +680,7 @@ suite('SQL Object Explorer Service tests', () => { sqlOEProvider.setup(x => x.expandNode(TypeMoq.It.is(x => x.nodePath === nodePath))).callback(() => { }).returns(() => Promise.resolve(true)); // If I queue a second expand request (the first compconstes normally because of the original mock) and then close the session - const rootNode = new TreeNode(NodeType.Root, '', '', false, objectExplorerSession.rootNode.nodePath, '', '', null, null, undefined, undefined); + const rootNode = new TreeNode(NodeType.Root, '', '', false, objectExplorerSession.rootNode.nodePath, '', '', '', null, null, undefined, undefined); await objectExplorerService.expandNode(mssqlProviderName, objectExplorerSession, rootNode); const expandPromise = objectExplorerService.expandNode(mssqlProviderName, objectExplorerSession, rootNode); const closeSessionResult = await objectExplorerService.closeSession(mssqlProviderName, objectExplorerSession); @@ -693,7 +697,7 @@ suite('SQL Object Explorer Service tests', () => { // If I call resolveTreeNodeChildren once, set an error on the node, and then call it again const tablesNodePath = 'testServerName/tables'; - const tablesNode = new TreeNode(NodeType.Folder, '', 'Tables', false, tablesNodePath, '', '', null, null, undefined, undefined); + const tablesNode = new TreeNode(NodeType.Folder, '', 'Tables', false, tablesNodePath, 'testServerName', '', '', null, null, undefined, undefined); tablesNode.connection = connection; await objectExplorerService.resolveTreeNodeChildren(objectExplorerSession, tablesNode); sqlOEProvider.verify(x => x.refreshNode(TypeMoq.It.is(x => x.nodePath === tablesNodePath)), TypeMoq.Times.never()); diff --git a/src/sql/workbench/test/electron-browser/api/extHostObjectExplorer.test.ts b/src/sql/workbench/test/electron-browser/api/extHostObjectExplorer.test.ts index 6aa3dab453..05fde86b40 100644 --- a/src/sql/workbench/test/electron-browser/api/extHostObjectExplorer.test.ts +++ b/src/sql/workbench/test/electron-browser/api/extHostObjectExplorer.test.ts @@ -14,6 +14,7 @@ const nodes: { [nodeName: string]: azdata.NodeInfo } = { 'Server1': { nodePath: 'MyServer', + parentNodePath: '', nodeStatus: '', nodeSubType: '', nodeType: 'Server', @@ -24,6 +25,7 @@ const nodes: { [nodeName: string]: azdata.NodeInfo } = }, 'DatabasesFolder': { nodePath: 'MyServer/Databases', + parentNodePath: 'MyServer', nodeStatus: '', nodeSubType: '', nodeType: 'Folder', @@ -34,6 +36,7 @@ const nodes: { [nodeName: string]: azdata.NodeInfo } = }, 'Database1': { nodePath: 'MyServer/Databases/MyDatabase', + parentNodePath: 'MyServer/Databases', nodeStatus: '', nodeSubType: '', nodeType: 'Database', @@ -44,6 +47,7 @@ const nodes: { [nodeName: string]: azdata.NodeInfo } = }, 'Database2': { nodePath: 'MyServer/Databases/My/TrickyDatabase', + parentNodePath: 'MyServer/Databases', nodeStatus: '', nodeSubType: '', nodeType: 'Database', @@ -54,6 +58,7 @@ const nodes: { [nodeName: string]: azdata.NodeInfo } = }, 'TablesFolder': { nodePath: 'MyServer/Databases/My/TrickyDatabase/Tables', + parentNodePath: 'MyServer/Databases/My/TrickyDatabase', nodeStatus: '', nodeSubType: '', nodeType: 'Folder',