diff --git a/package.json b/package.json index ef951e2ce..0bdf3366b 100644 --- a/package.json +++ b/package.json @@ -497,6 +497,12 @@ "command": "cosmosDB.openTrigger", "title": "Open Trigger" }, + { + "category": "Cosmos DB", + "command": "cosmosDB.launchCosmosShell", + "title": "Launch Cosmos Shell", + "when": "vscodeDatabases.cosmosShellSupportEnabled" + }, { "category": "NoSQL", "command": "cosmosDB.viewDocDBCollectionOffer", @@ -1020,6 +1026,61 @@ "when": "view =~ /azure(ResourceGroups|Workspace|FocusView)/ && viewItem =~ /postgresDatabase(?![a-z])/i", "group": "2@1" }, + { + "command": "cosmosDB.launchCosmosShell", + "when": "view =~ /(azureResourceGroups|azureFocusView)/ && viewItem =~ /cosmosDBMongoServer(?![a-z])/i", + "group": "2@1" + }, + { + "command": "cosmosDB.launchCosmosShell", + "when": "view =~ /(azureResourceGroups|azureFocusView)/ && viewItem =~ /cosmosDBGraphAccount(?![a-z])/i", + "group": "2@1" + }, + { + "command": "cosmosDB.launchCosmosShell", + "when": "view =~ /(azureResourceGroups|azureFocusView)/ && viewItem =~ /cosmosDBDocumentServer(?![a-z])/i", + "group": "2@1" + }, + { + "command": "cosmosDB.launchCosmosShell", + "when": "view =~ /(azureResourceGroups|azureFocusView)/ && viewItem =~ /cosmosDBTableAccount(?![a-z])/i", + "group": "2@1" + }, + { + "command": "cosmosDB.launchCosmosShell", + "when": "view == azureWorkspace && viewItem == cosmosDBMongoServerAttached", + "group": "2@1" + }, + { + "command": "cosmosDB.launchCosmosShell", + "when": "view == azureWorkspace && viewItem == cosmosDBGraphAccountAttached", + "group": "2@1" + }, + { + "command": "cosmosDB.launchCosmosShell", + "when": "view == azureWorkspace && viewItem == cosmosDBDocumentServerAttached", + "group": "2@1" + }, + { + "command": "cosmosDB.launchCosmosShell", + "when": "view == azureWorkspace && viewItem == cosmosDBTableAccountAttached", + "group": "2@1" + }, + { + "command": "cosmosDB.launchCosmosShell", + "when": "view =~ /azure(ResourceGroups|Workspace|FocusView)/ && viewItem =~ /postgresDatabase(?![a-z])/i", + "group": "2@1" + }, + { + "command": "cosmosDB.launchCosmosShell", + "when": "view =~ /azure(ResourceGroups|Workspace|FocusView)/ && viewItem == cosmosDBDocumentDatabase", + "group": "2@1" + }, + { + "command": "cosmosDB.launchCosmosShell", + "when": "view =~ /azure(ResourceGroups|Workspace|FocusView)/ && viewItem == cosmosDBDocumentCollection", + "group": "2@1" + }, { "command": "azureDatabases.refresh", "when": "view == azureWorkspace && viewItem == cosmosDBMongoServerAttached", @@ -1028,7 +1089,7 @@ { "command": "azureDatabases.refresh", "when": "view =~ /azure(ResourceGroups|Workspace|FocusView)/ && viewItem == cosmosDBDocumentCollection", - "group": "2@1" + "group": "3@1" }, { "command": "azureDatabases.refresh", diff --git a/src/cosmosShell/CosmosShellExtension.ts b/src/cosmosShell/CosmosShellExtension.ts new file mode 100644 index 000000000..d499969f4 --- /dev/null +++ b/src/cosmosShell/CosmosShellExtension.ts @@ -0,0 +1,97 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +/** + * entry-point for mongoClusters-related code. Activated from ./src/extension.ts + * + * We'll try to have everything related to mongoClusters-support managed from here. + * In case of a failure with this plan, this comment section will be updated. + */ +import { + callWithTelemetryAndErrorHandling, + registerCommandWithTreeNodeUnwrapping, + type IActionContext, +} from '@microsoft/vscode-azext-utils'; +import * as child from 'child_process'; +import * as vscode from 'vscode'; +import { ext } from '../extensionVariables'; +// eslint-disable-next-line import/consistent-type-specifier-style +import { DocDBAccountTreeItemBase } from '../docdb/tree/DocDBAccountTreeItemBase'; +import { DocDBCollectionTreeItem } from '../docdb/tree/DocDBCollectionTreeItem'; +import { DocDBDatabaseTreeItem } from '../docdb/tree/DocDBDatabaseTreeItem'; +import { MongoAccountTreeItem } from '../mongo/tree/MongoAccountTreeItem'; + +export class CosmosShellExtension implements vscode.Disposable { + dispose(): Promise { + return Promise.resolve(); + } + + async activate(): Promise { + await callWithTelemetryAndErrorHandling('cosmosDB.cosmosShell.activate', (_activateContext: IActionContext) => { + const isCosmosShellInstalled: boolean = isCosmosShellSupportEnabled(); + vscode.commands.executeCommand( + 'setContext', + 'vscodeDatabases.cosmosShellSupportEnabled', + isCosmosShellInstalled, + ); + + if (isCosmosShellInstalled) { + // registerCommand('cosmosDB.launchCosmosShell', launchCosmosShell); + registerCommandWithTreeNodeUnwrapping('cosmosDB.launchCosmosShell', launchCosmosShell); + ext.outputChannel.appendLine(`Cosmos Shell Extension: activated.`); + } else { + ext.outputChannel.appendLine(`Cosmos Shell Extension: deactivated.`); + } + }); + } +} + +function getCosmosShellCommand(): string { + return process.env.COSMOS_SHELL_PATH || 'CosmosShell'; +} + +export function launchCosmosShell( + _context: IActionContext, + node?: MongoAccountTreeItem | DocDBAccountTreeItemBase | DocDBCollectionTreeItem, +) { + const command = getCosmosShellCommand(); + if (!node) { + const terminal: vscode.Terminal = vscode.window.createTerminal('Cosmos Shell', command); + terminal.show(); + return; + } + let connectionString; + if (node instanceof MongoAccountTreeItem || node instanceof DocDBAccountTreeItemBase) { + connectionString = node.connectionString; + } else { + connectionString = node.parent.connectionString; + } + const terminal: vscode.Terminal = vscode.window.createTerminal('Cosmos Shell', command, [ + '--connect', + connectionString, + ]); + terminal.show(); + if (node instanceof DocDBCollectionTreeItem) { + terminal.sendText('cd ' + node.parent.label + '/' + node.label, true); + } else if (node instanceof DocDBDatabaseTreeItem) { + terminal.sendText('cd ' + node.label, true); + } +} + +/** + * Determines if CosmosShell is installed. + * + * @returns true, if CosmosShell is installed, false otherwise. + */ +export function isCosmosShellSupportEnabled(): boolean { + const command = getCosmosShellCommand(); + try { + child.execFileSync(command, ['--version']); + return true; + } catch (err) { + ext.outputChannel.appendLine('fail ' + err); + return false; + } +} diff --git a/src/extension.ts b/src/extension.ts index 3f769b93d..0dd15b7bb 100644 --- a/src/extension.ts +++ b/src/extension.ts @@ -38,6 +38,7 @@ import { doubleClickDebounceDelay, sqlFilter, } from './constants'; +import { CosmosShellExtension } from './cosmosShell/CosmosShellExtension'; import { DatabasesFileSystem } from './DatabasesFileSystem'; import { registerDocDBCommands } from './docdb/registerDocDBCommands'; import { DocDBAccountTreeItem } from './docdb/tree/DocDBAccountTreeItem'; @@ -124,6 +125,9 @@ export async function activateInternal( context.subscriptions.push(mongoClustersSupport); // to be disposed when extension is deactivated. await mongoClustersSupport.activate(); + const cosmosShellSupport: CosmosShellExtension = new CosmosShellExtension(); + await cosmosShellSupport.activate(); + context.subscriptions.push( vscode.workspace.registerFileSystemProvider(DatabasesFileSystem.scheme, ext.fileSystem), );