diff --git a/README.md b/README.md index bf29e7f..09acdda 100644 --- a/README.md +++ b/README.md @@ -54,9 +54,15 @@ The extensions can be downloaded from the official Visual Studio Code extension # Release Notes -**v1.2.1:** +**v1.2.3:** +- Databricks Kernels are now moved to the top when opening a notebook from the local sync folder or via `dbws:/` +- added `Insert` icons for Secrets and SecretScopes to easily add the code snippet to the current editor +- fixed an issue with `%run` + +**v1.2.2:** - added `Restart Kernel` to Databricks notebooks - added details specs of cluster to Notebook Kernel selector +- fixed an issue where the extension did not load when no default connection was specified - fixed an issue with `%run` and absolute paths ([#93](/../../issues/93)) - fixed an issue with `Files in Repos` ([#101](/../../issues/101)) - fixed an issues with CLI Connection Manager ([#99](/../../issues/99)) @@ -104,55 +110,6 @@ The extensions can be downloaded from the official Visual Studio Code extension - improved logging and output of messages - use native VSCode icons instead of custom ones -**v0.9.4:** -- added support for VS Code setting `http.proxyStrictSSL` to optionally allow invalid certificates from the proxy. Can only be used in combination with `http.proxySupport = "on"`. -- use VS Code native secret management instead of `KeyTar` -- update Dev Dependencies to VS Code 1.66 -- fix minor issue with SQL Browser refresh -- support for environment variables in `localSyncFolder` - -**v0.9.3:** -- added `Copy Path` feature for Workspace and DBFS browser -- added Folders for Repositories as in the Databricks Web UI - -**v0.9.2:** -- Make use of Databricks [Jobs API v2.1](https://docs.databricks.com/dev-tools/api/latest/jobs.html) - -**v0.9.1:** -- fix issue when opening `.ipynb` files/notebooks - -**v0.9.0:** -- Added support for [Repos API](https://docs.databricks.com/dev-tools/api/latest/repos.html) -- added new Repos tab -- switching branches -- deleteing a repo - -**v0.8.9:** -- add support to list regular files from Git repositories. Up-/Download is not yet supported by the underlying API - -**v0.8.8:** -- fix issues with Databricks running on GCP - -**v0.8.7:** -- fix issue with repositories in workspace - -**v0.8.6:** -- added support for Databricks CLI profiles - - use Databricks CLI profiles to manage connections - - support `DATABRICKS_CONFIG_FILE` for custom config file locations -- further improvements to the SQL Data browser - - Show Definition - - Datatypes for columns including complex columns (array, struct, map) - - Table and column comments - - improved tooltips and desciption -- improved tooltip for Connections -- fixed an issue with upload of whole workspace folders -- fixed an issue with Azure KeyVault secret scopes - -**v0.8.0:** -- add SQL Data browser as in the Databricks UI -- fixed an issue with Secrets - you can now add/delete secrets again - # Outlook upcoming/planned features - Support for Drag&Drop of local files directly into the workspace - Fix remaining issues with Notebook Kernel and code cells @@ -288,7 +245,7 @@ The cluster manager also distinguishes between regular user-created clusters and Using Databricks Notebook Kernels you can execute local code againt a running Databricks cluster. Simply open a `.ipynb` notebook and select the Databricks kernel of your choice. A new kernel will be added automatically for each Databricks cluster that is currently running. In case you need to restart the kernel, you can do so by right-clicking the underlying cluster in the [Cluster Manager](#cluster-manager) and select `Restart Databricks Kernel`. The execution of the first cell may take a bit longer as the remote execution context has to be set up before any commands can be executed. The notebook kernel supports the magics `%sql`, `%python`, `%r`, `%scala`, `%md` and also `%pip`. Depending on the output of the cell, the results are rendered accordingly - e.g. as text, as table or as image. Due to the limitations of the underlying APIs, the output is limited to one output type so you should avoid mixing `print(...)`, `display(df)` and `df.plot` as only one of them will be show. -If you need richer output especially for tables, you can install additional Notebook Renderer Extensions. We recommend [vscode-data-table](https://github.com/RandomFractals/vscode-data-table). Unfortunately the author decided to unpublish the extension from the offical VSCode market place but it can still be downloaded the `.vsix` from the [Github Releases](https://github.com/RandomFractals/vscode-data-table/releases) +For better visualization of tabluar results this extension includes a dependency to the extension [vscode-data-table](https://github.com/RandomFractals/vscode-data-table) which offers much more features than the standard visualizations. # File System Integration ![File System Integration](/images/FileSystemIntegration.jpg?raw=true "File System Integration") diff --git a/package.json b/package.json index ad47228..cbd6cb4 100644 --- a/package.json +++ b/package.json @@ -2,7 +2,7 @@ "name": "databricks-vscode", "displayName": "Databricks VSCode", "description": "Extension to manage your Databricks workspace and ease local development", - "version": "1.2.1", + "version": "1.2.3", "publisher": "paiqo", "icon": "resources/databricks_extension.png", "author": { @@ -59,6 +59,9 @@ "onFileSystem:dbfs", "onFileSystem:dbws" ], + "extensionDependencies": [ + "RandomFractalsInc.vscode-data-table" + ], "main": "./dist/node/extension.js", "browser_NOT_WORKING_YET": "./dist/web/extension.js", "contributes": { @@ -669,6 +672,11 @@ "title": "Delete", "icon": "$(remove-close)" }, + { + "command": "databricksSecret.insertCode", + "title": "Insert", + "icon": "$(code)" + }, { "command": "databricksSQL.refresh", "title": "Refresh", @@ -926,6 +934,11 @@ "when": "view == databricksSecrets && viewItem == SECRET_DATABRICKS", "group": "inline@99" }, + { + "command": "databricksSecret.insertCode", + "when": "view == databricksSecrets && viewItem =~ /^SECRET_.*/", + "group": "inline@90" + }, { "command": "databricksSQL.refresh", "when": "view == databricksSQL && viewItem == DATABASE", diff --git a/src/extension.ts b/src/extension.ts index e0d6545..0b31e9a 100644 --- a/src/extension.ts +++ b/src/extension.ts @@ -144,6 +144,7 @@ export async function activate(context: vscode.ExtensionContext) { vscode.commands.registerCommand('databricksSecretScope.addSecret', (scope: DatabricksSecretScope) => scope.addSecret()); vscode.commands.registerCommand('databricksSecret.update', (secret: DatabricksSecret) => secret.update()); vscode.commands.registerCommand('databricksSecret.delete', (secret: DatabricksSecret) => secret.delete()); + vscode.commands.registerCommand('databricksSecret.insertCode', (scope: DatabricksSecret) => scope.insertCode(scope)); // register DatabricksSQLTreeProvider let databricksSQLTreeProvider = new DatabricksSQLTreeProvider(); diff --git a/src/vscode/notebook/DatabricksKernel.ts b/src/vscode/notebook/DatabricksKernel.ts index c0a3c2a..569bcf0 100644 --- a/src/vscode/notebook/DatabricksKernel.ts +++ b/src/vscode/notebook/DatabricksKernel.ts @@ -60,12 +60,20 @@ export class DatabricksKernel implements vscode.NotebookController { this._controller.supportsExecutionOrder = this.supportsExecutionOrder; this._controller.executeHandler = this.executeHandler.bind(this); this._controller.dispose = this.disposeController.bind(this); - //this._controller.updateNotebookAffinity = this.updateNotebookAffinity.bind(this); - this._controller.onDidChangeSelectedNotebooks(this._onDidChangeSelectedNotebooks, this, []); + + vscode.workspace.onDidOpenNotebookDocument((event) => this._onDidOpenNotebookDocument(event)); ThisExtension.PushDisposable(this); } + async _onDidOpenNotebookDocument(notebook: vscode.NotebookDocument) { + // set this controller as recommended Kernel for notebooks opened via dbws:/ file system or from or local sync folder + if (notebook.uri.scheme == "dbws" || notebook.uri.toString().startsWith(ThisExtension.ActiveConnection.localSyncFolder.toString())) { + this._controller.updateNotebookAffinity(notebook, vscode.NotebookControllerAffinity.Preferred); + } + } + + // #region Cluster-properties static getId(clusterId: string, kernelType: KernelType) { return this.baseId + clusterId + "-" + kernelType; } @@ -89,17 +97,14 @@ export class DatabricksKernel implements vscode.NotebookController { details += "Head: " + this._cluster.driver_node_type_id; if (this._cluster.num_workers != undefined) { - if(this._cluster.num_workers > 0) - { - details += " - " + this._cluster.num_workers + " Worker(s): " + this._cluster.node_type_id; + if (this._cluster.num_workers > 0) { + details += " - " + this._cluster.num_workers + " Worker(s): " + this._cluster.node_type_id; } - else - { + else { details += " (SingleNode)"; } } - else if(this._cluster.autoscale) - { + else if (this._cluster.autoscale) { details += " - " + this._cluster.executors.length + " (" + this._cluster.autoscale.min_workers + "-" + this._cluster.autoscale.max_workers + ")"; details += " Worker(s): " + this._cluster.node_type_id; } @@ -111,11 +116,11 @@ export class DatabricksKernel implements vscode.NotebookController { return this._language; } - private get ClusterRuntime(): {major: number, minor: number} { + private get ClusterRuntime(): { major: number, minor: number } { let tokens: string[] = this._cluster.spark_version.split("-")[0].split(".") - return { major: +tokens[0], minor: +tokens[1]} + return { major: +tokens[0], minor: +tokens[1] } } - + // #endregion async disposeController(): Promise { for (let context of this._executionContexts.entries()) { @@ -183,12 +188,11 @@ export class DatabricksKernel implements vscode.NotebookController { vscode.window.showErrorMessage("Could not setup Files in Repo for '" + notebookUri + "' on cluster '" + this.ClusterID + "'\nPlease check logs for details!"); } - else - { + else { ThisExtension.log(`NotebookKernel: Successfully set up FilesInRepo by running '${commandTexts[2]}' and '${commandTexts[3]}' on driver!`); ThisExtension.setStatusBar("FilesInRepo initialized!"); } - + } } @@ -198,18 +202,7 @@ export class DatabricksKernel implements vscode.NotebookController { } interruptHandler?: (notebook: vscode.NotebookDocument) => void | Thenable; readonly onDidChangeSelectedNotebooks: vscode.Event<{ readonly notebook: vscode.NotebookDocument; readonly selected: boolean; }>; - private async _onDidChangeSelectedNotebooks(event: { notebook: vscode.NotebookDocument; selected: boolean }) { - // TODO: this could should help to make the Databricks Kernel more prominent for notebooks loaded via Databricks extension - if (event.notebook.uri.scheme == "dbws" || event.notebook.uri.toString().startsWith(ThisExtension.ActiveConnection.localSyncFolder.toString())) { - this._controller.updateNotebookAffinity(event.notebook, vscode.NotebookControllerAffinity.Preferred); - } - } - updateNotebookAffinity(notebook: vscode.NotebookDocument, affinity: vscode.NotebookControllerAffinity): void { - - //let x = "updateNotebookAffinity"; - - //ThisExtension.log(x); - } + updateNotebookAffinity(notebook: vscode.NotebookDocument, affinity: vscode.NotebookControllerAffinity): void { } @@ -297,6 +290,7 @@ export class DatabricksKernel implements vscode.NotebookController { switch (cell.notebook.uri.scheme) { case "dbws": runUri = vscode.Uri.file(runFile); + runUri = runUri.with({ scheme: "dbws" }) break; case "file": runUri = await FSHelper.joinPath(ThisExtension.ActiveConnection.localSyncFolder, ThisExtension.ActiveConnection.localSyncSubfolders.Workspace, runFile); @@ -307,40 +301,61 @@ export class DatabricksKernel implements vscode.NotebookController { } ThisExtension.log("Executing %run for '" + runUri + "' ..."); - switch (runUri.scheme) { - case "dbws": - // we cannot use vscode.workspace.fs here as we need the SOURCE file and not the ipynb file - commandText = Buffer.from(await DatabricksApiService.downloadWorkspaceItem(runUri.path, "SOURCE")).toString(); - break; - case "file": - // %run is not aware of filetypes/extensions/langauges so we need to check which ones actually exist locally - let allFiles = await vscode.workspace.fs.readDirectory(FSHelper.parent(runUri)); - let relevantFiles = allFiles.filter(x => x[0].startsWith(FSHelper.basename(runUri))); - - if (relevantFiles.length == 1) { - runUri = vscode.Uri.joinPath(FSHelper.parent(runUri), relevantFiles[0][0]); - commandText = Buffer.from(await vscode.workspace.fs.readFile(runUri)).toString(); - - if (FSHelper.extension(runUri) == ".ipynb") { - let notebookJSON = JSON.parse(commandText); - let cells = notebookJSON["cells"]; - cells = cells.filter(x => x["cell_type"] == "code"); // only take code cells - - let commands: string[] = []; - for (let cell of cells) { - commands.push(cell["source"].join("\n")); - } + try { + + switch (runUri.scheme) { + case "dbws": + if (!await FSHelper.pathExists(runUri)) { + throw vscode.FileSystemError.FileNotFound(runUri); + } + // we cannot use vscode.workspace.fs here as we need the SOURCE file and not the ipynb file + commandText = Buffer.from(await DatabricksApiService.downloadWorkspaceItem(runUri.path, "SOURCE")).toString(); + break; + + case "file": + // %run is not aware of filetypes/extensions/langauges so we need to check which ones actually exist locally + let allFiles = await vscode.workspace.fs.readDirectory(FSHelper.parent(runUri)); + let relevantFiles = allFiles.filter(x => x[0].startsWith(FSHelper.basename(runUri))); + + if (relevantFiles.length == 1) { + runUri = vscode.Uri.joinPath(FSHelper.parent(runUri), relevantFiles[0][0]); + commandText = Buffer.from(await vscode.workspace.fs.readFile(runUri)).toString(); - commandText = commands.join("\n"); + if (FSHelper.extension(runUri) == ".ipynb") { + let notebookJSON = JSON.parse(commandText); + let cells = notebookJSON["cells"]; + cells = cells.filter(x => x["cell_type"] == "code"); // only take code cells + + let commands: string[] = []; + for (let cell of cells) { + commands.push(cell["source"].join("\n")); + } + + commandText = commands.join("\n"); + } + } + else { + throw vscode.FileSystemError.FileNotFound(runUri); } - } - else { - throw vscode.FileSystemError.FileNotFound(runUri); - } - break; - default: - throw new Error("%run is not supported for FileSystem scheme '" + runUri.scheme + "'!"); + break; + default: + throw new Error("%run is not supported for FileSystem scheme '" + runUri.scheme + "'!"); + } } + catch (error) { + execution.appendOutput(new vscode.NotebookCellOutput([ + vscode.NotebookCellOutputItem.text(error.name + ": " + error.message, 'text/html') + ])); + + execution.appendOutput(new vscode.NotebookCellOutput([ + vscode.NotebookCellOutputItem.error(error), + ])); + execution.end(false, Date.now()); + return; + } + + + let outputRun: vscode.NotebookCellOutput = new vscode.NotebookCellOutput([ vscode.NotebookCellOutputItem.text(commandText, 'text/plain') ]) diff --git a/src/vscode/treeviews/connections/DatabricksConnectionManagerVSCode.ts b/src/vscode/treeviews/connections/DatabricksConnectionManagerVSCode.ts index 5b60de4..dabf4b5 100644 --- a/src/vscode/treeviews/connections/DatabricksConnectionManagerVSCode.ts +++ b/src/vscode/treeviews/connections/DatabricksConnectionManagerVSCode.ts @@ -6,9 +6,9 @@ import { AccessTokenSecure } from './_types'; import { DatabricksConnectionManager } from './DatabricksConnectionManager'; import { DatabricksConnectionTreeItem } from './DatabricksConnectionTreeItem'; -export class DatabricksConnectionManagerVSCode extends DatabricksConnectionManager{ +export class DatabricksConnectionManagerVSCode extends DatabricksConnectionManager { - private _settingScope: ConfigSettingSource; + private _settingScope: ConfigSettingSource; constructor() { super(); @@ -30,8 +30,7 @@ export class DatabricksConnectionManagerVSCode extends DatabricksConnectionManag else { this._lastActiveConnectionName = ThisExtension.getConfigurationSetting("databricks.lastActiveConnection", this._settingScope).value; - if(!this._lastActiveConnectionName) - { + if (!this._lastActiveConnectionName) { ThisExtension.log("Setting 'databricks.lastActiveConnection' is not set - using first available connection instead!"); this._lastActiveConnectionName = this._connections[0].displayName; } @@ -67,45 +66,50 @@ export class DatabricksConnectionManagerVSCode extends DatabricksConnectionManag ThisExtension.log("Loading Connections from 'databricks.connections' ..."); let connections = ThisExtension.getConfigurationSetting('databricks.connections', this._settingScope, true); - connections.value.forEach( x => x._source = "databricks.connections"); + connections.value.forEach(x => x._source = "databricks.connections"); connections.value.forEach(x => DatabricksConnectionTreeItem.validate(x, false)); this._connections = this._connections.concat(connections.value); - ThisExtension.log("Loading Connection from 'databricks.default.*' ..."); - let defaultConnection: iDatabricksConnection = { - "displayName": ThisExtension.getConfigurationSetting('databricks.connection.default.displayName', this._settingScope).value, - "apiRootUrl": vscode.Uri.parse(ThisExtension.getConfigurationSetting('databricks.connection.default.apiRootUrl', this._settingScope).value), - "localSyncFolder": vscode.Uri.file(ThisExtension.getConfigurationSetting('databricks.connection.default.localSyncFolder', this._settingScope).value), - "localSyncSubfolders": ThisExtension.getConfigurationSetting('databricks.connection.default.localSyncSubfolders', this._settingScope).value, - "personalAccessToken": ThisExtension.getConfigurationSetting('databricks.connection.default.personalAccessToken', this._settingScope).value, - "personalAccessTokenSecure": ThisExtension.getConfigurationSetting('databricks.connection.default.personalAccessTokenSecure', this._settingScope).value, - "exportFormats": ThisExtension.getConfigurationSetting('databricks.connection.default.exportFormats', this._settingScope).value, - "useCodeCells": ThisExtension.getConfigurationSetting('databricks.connection.default.useCodeCells', this._settingScope).value, - "_source": "databricks.default" - }; - - if(DatabricksConnectionTreeItem.validate(defaultConnection, false)) - { - defaultConnection._source = "databricks.default"; - this._connections.push(defaultConnection); + ThisExtension.log("Loading Connection from 'databricks.connection.default.*' ..."); + try { + let defaultApiRootUrl = ThisExtension.getConfigurationSetting('databricks.connection.default.apiRootUrl', this._settingScope); + if (defaultApiRootUrl.value) { + let defaultConnection: iDatabricksConnection = { + "displayName": ThisExtension.getConfigurationSetting('databricks.connection.default.displayName', this._settingScope).value, + "apiRootUrl": vscode.Uri.parse(ThisExtension.getConfigurationSetting('databricks.connection.default.apiRootUrl', this._settingScope).value), + "localSyncFolder": vscode.Uri.file(ThisExtension.getConfigurationSetting('databricks.connection.default.localSyncFolder', this._settingScope).value), + "localSyncSubfolders": ThisExtension.getConfigurationSetting('databricks.connection.default.localSyncSubfolders', this._settingScope).value, + "personalAccessToken": ThisExtension.getConfigurationSetting('databricks.connection.default.personalAccessToken', this._settingScope).value, + "personalAccessTokenSecure": ThisExtension.getConfigurationSetting('databricks.connection.default.personalAccessTokenSecure', this._settingScope).value, + "exportFormats": ThisExtension.getConfigurationSetting('databricks.connection.default.exportFormats', this._settingScope).value, + "useCodeCells": ThisExtension.getConfigurationSetting('databricks.connection.default.useCodeCells', this._settingScope).value, + "_source": "databricks.default" + }; + + if (DatabricksConnectionTreeItem.validate(defaultConnection, false)) { + defaultConnection._source = "databricks.default"; + this._connections.push(defaultConnection); + } + } + } + catch (error) { + let msg = "Could not load connection from 'databricks.connection.default.*' settings!"; + ThisExtension.log("ERROR: " + msg); + vscode.window.showErrorMessage(msg); } - } + } updateConnection(updatedCon: iDatabricksConnection): void { - if(updatedCon._source == "databricks.default") - { + if (updatedCon._source == "databricks.default") { ThisExtension.updateConfigurationSetting("databricks.connection.default.personalAccessToken", undefined, ThisExtension.SettingScope); ThisExtension.updateConfigurationSetting("databricks.connection.default.personalAccessTokenSecure", updatedCon.personalAccessTokenSecure, ThisExtension.SettingScope); } - else - { + else { let configConnections = ThisExtension.getConfigurationSetting("databricks.connections", ThisExtension.SettingScope); - for (let con of configConnections.value) - { - if(con.displayName == updatedCon.displayName) - { + for (let con of configConnections.value) { + if (con.displayName == updatedCon.displayName) { con.personalAccessToken = undefined; con.personalAccessTokenSecure = updatedCon.personalAccessTokenSecure; } diff --git a/src/vscode/treeviews/secrets/DatabricksSecret.ts b/src/vscode/treeviews/secrets/DatabricksSecret.ts index 85f6791..108588e 100644 --- a/src/vscode/treeviews/secrets/DatabricksSecret.ts +++ b/src/vscode/treeviews/secrets/DatabricksSecret.ts @@ -46,7 +46,7 @@ export class DatabricksSecret extends DatabricksSecretTreeItem { return this._secret; } - get dragAndDropText(): string { + get codeText(): string { return `dbutils.secrets.get(scope="${this.scope}", key="${this.secret}")`; } @@ -72,4 +72,18 @@ export class DatabricksSecret extends DatabricksSecretTreeItem { setTimeout(() => this.refreshParent(), 500); } } + + async insertCode(item: DatabricksSecretTreeItem): Promise { + const editor = vscode.window.activeTextEditor; + if (editor === undefined) { + return; + } + + const start = editor.selection.start; + const end = editor.selection.end; + const range = new vscode.Range(start.line, start.character, end.line, end.character); + await editor.edit((editBuilder) => { + editBuilder.replace(range, item.codeText); + }); + } } \ No newline at end of file diff --git a/src/vscode/treeviews/secrets/DatabricksSecretDragAndDropController.ts b/src/vscode/treeviews/secrets/DatabricksSecretDragAndDropController.ts index 3000936..7eea7b5 100644 --- a/src/vscode/treeviews/secrets/DatabricksSecretDragAndDropController.ts +++ b/src/vscode/treeviews/secrets/DatabricksSecretDragAndDropController.ts @@ -14,12 +14,8 @@ class DatabricksSecretObjectTransferItem extends vscode.DataTransferItem { super(_nodes); } - asObject(): readonly DatabricksSecretTreeItem[] { - return this._nodes; - } - asString(): Promise { - return this.value[0].dragAndDropText; + return this.value[0].codeText; } } @@ -27,12 +23,13 @@ class DatabricksSecretObjectTransferItem extends vscode.DataTransferItem { export class DatabricksSecretDragAndDropController implements vscode.TreeDragAndDropController { dropMimeTypes: readonly string[] = ["application/vnd.code.tree.databrickssecrets", "text/uri-list"]; - dragMimeTypes: readonly string[] = ["application/vnd.code.tree.databrickssecrets", "text/plain"]; + dragMimeTypes: readonly string[] = ["application/vnd.code.tree.databrickssecrets", "text/plain", "codeeditors"]; public async handleDrag?(source: readonly DatabricksSecretTreeItem[], dataTransfer: vscode.DataTransfer, token: vscode.CancellationToken): Promise { - dataTransfer.set("application/vnd.code.tree.databrickssecrets", new DatabricksSecretObjectTransferItem(source)); + //dataTransfer.set("application/vnd.code.tree.databrickssecrets", new DatabricksSecretObjectTransferItem(source)); //dataTransfer.set("application/vnd.code.tree.databrickssecrets", new vscode.DataTransferItem(source)); - dataTransfer.set("text/plain", new vscode.DataTransferItem(source[0].dragAndDropText)); + dataTransfer.set("text/plain", new vscode.DataTransferItem(source)); + dataTransfer.set("codeeditors", new vscode.DataTransferItem(source)); } public async handleDrop?(target: DatabricksSecretTreeItem, dataTransfer: vscode.DataTransfer, token: vscode.CancellationToken): Promise { diff --git a/src/vscode/treeviews/secrets/DatabricksSecretScope.ts b/src/vscode/treeviews/secrets/DatabricksSecretScope.ts index c115e79..bfcd53c 100644 --- a/src/vscode/treeviews/secrets/DatabricksSecretScope.ts +++ b/src/vscode/treeviews/secrets/DatabricksSecretScope.ts @@ -67,7 +67,7 @@ export class DatabricksSecretScope extends DatabricksSecretTreeItem { return secretItems; } - get dragAndDropText(): string { + get codeText(): string { return `dbutils.secrets.list("${this.Scope}")`; } diff --git a/src/vscode/treeviews/secrets/DatabricksSecretTreeItem.ts b/src/vscode/treeviews/secrets/DatabricksSecretTreeItem.ts index b42897f..b37b58f 100644 --- a/src/vscode/treeviews/secrets/DatabricksSecretTreeItem.ts +++ b/src/vscode/treeviews/secrets/DatabricksSecretTreeItem.ts @@ -38,12 +38,13 @@ export class DatabricksSecretTreeItem extends vscode.TreeItem { } - get dragAndDropText(): string { - // needs to be overwritten in derived class - throw new Error("Not implemented!"); - } - async refreshParent(): Promise { vscode.commands.executeCommand("databricksSecrets.refresh", this.parent, false); } + + + get codeText(): string { + // needs to be overwritten in derived class + throw new Error("Not implemented!"); + } } \ No newline at end of file diff --git a/src/vscode/treeviews/secrets/DatabricksSecretTreeProvider.ts b/src/vscode/treeviews/secrets/DatabricksSecretTreeProvider.ts index 34fa152..b86d989 100644 --- a/src/vscode/treeviews/secrets/DatabricksSecretTreeProvider.ts +++ b/src/vscode/treeviews/secrets/DatabricksSecretTreeProvider.ts @@ -14,18 +14,29 @@ export class DatabricksSecretTreeProvider implements vscode.TreeDataProvider = new vscode.EventEmitter(); readonly onDidChangeTreeData: vscode.Event = this._onDidChangeTreeData.event; - constructor(context: vscode.ExtensionContext) { - const view = vscode.window.createTreeView('databricksSecrets', { treeDataProvider: this, showCollapseAll: true, canSelectMany: true, dragAndDropController: new DatabricksSecretDragAndDropController() }); - context.subscriptions.push(view); - - view.onDidChangeSelection((event) => this._onDidChangeSelection(event.selection)); + private _treeView: vscode.TreeView; + constructor(context: vscode.ExtensionContext) { + const treeView = vscode.window.createTreeView('databricksSecrets', { + treeDataProvider: this, + showCollapseAll: true, + canSelectMany: false, + dragAndDropController: new DatabricksSecretDragAndDropController() }); + context.subscriptions.push(treeView); + + treeView.onDidChangeSelection((event) => this._onDidChangeSelection(event.selection)); + treeView.onDidExpandElement((event) => this._onDidExpandElement(event.element)); + treeView.onDidCollapseElement((event) => this._onDidCollapseElement(event.element)); + treeView.onDidChangeVisibility((event) => this._onDidChangeVisibility(event.visible)); + + this._treeView = treeView; //ThisExtension.TreeViewWorkspaces = this; } - private async _onDidChangeSelection(items: readonly DatabricksSecretTreeItem[]): Promise - { - } + private async _onDidChangeSelection(items: readonly DatabricksSecretTreeItem[]): Promise { } + private async _onDidExpandElement(item: DatabricksSecretTreeItem): Promise { } + private async _onDidCollapseElement(item: DatabricksSecretTreeItem): Promise { } + private async _onDidChangeVisibility(visible: boolean): Promise { } async refresh(showInfoMessage: boolean = false): Promise { if (showInfoMessage) { @@ -34,7 +45,7 @@ export class DatabricksSecretTreeProvider implements vscode.TreeDataProvider { + async getTreeItem(element: DatabricksSecretTreeItem): Promise { return element; }