From ba41e926c4c2b9b2e9dd80bd405fd0ecac3d734b Mon Sep 17 00:00:00 2001 From: Amir Omidi Date: Fri, 10 Apr 2020 15:12:57 -0700 Subject: [PATCH] Handle first time use when user doesn't have a cloud shell (#9890) * Handle first time use when user doesn't have a cloud shell * Catch errors * Update code * Update string per PM feedback * Update the internal URI --- .../azureResource/services/terminalService.ts | 94 ++++++++++++++----- 1 file changed, 71 insertions(+), 23 deletions(-) diff --git a/extensions/azurecore/src/azureResource/services/terminalService.ts b/extensions/azurecore/src/azureResource/services/terminalService.ts index b7f6a7e91c..9553b7acb2 100644 --- a/extensions/azurecore/src/azureResource/services/terminalService.ts +++ b/extensions/azurecore/src/azureResource/services/terminalService.ts @@ -5,13 +5,42 @@ import * as vscode from 'vscode'; import * as nls from 'vscode-nls'; -import axios, { AxiosRequestConfig } from 'axios'; +import axios, { AxiosRequestConfig, AxiosResponse } from 'axios'; import * as WS from 'ws'; import { IAzureTerminalService } from '../interfaces'; import { AzureAccount, AzureAccountSecurityToken, Tenant } from '../../account-provider/interfaces'; const localize = nls.loadMessageBundle(); + + +const handleNeverUsed = async (): Promise => { + const neverUsedString = localize('azure.coudTerminal.neverUsed', "If you have not launched Azure Cloud Shell from this account before, please visit https://shell.azure.com/ to get started. Once you are set up, you can use AzureCloud Shell directly in Azure Data Studio."); + enum TerminalOption { + OPEN_SITE, + OK + } + interface TerminalMessageItem extends vscode.MessageItem { + action: TerminalOption; + } + + const openAzureShellButton: TerminalMessageItem = { + action: TerminalOption.OPEN_SITE, + title: localize('azure.cloudTerminal.openAzureShell', "Open Azure Shell") + }; + + const okButton: TerminalMessageItem = { + action: TerminalOption.OK, + title: localize('azure.cloudTerminal.ok', "OK") + }; + + const option = await vscode.window.showInformationMessage(neverUsedString, openAzureShellButton, okButton); + + if (option.action === TerminalOption.OPEN_SITE) { + vscode.env.openExternal(vscode.Uri.parse('https://aka.ms/AA83f8f')); + } +}; + export class AzureTerminalService implements IAzureTerminalService { private readonly apiVersion = '?api-version=2018-10-01'; @@ -31,9 +60,16 @@ export class AzureTerminalService implements IAzureTerminalService { }; const metadata = account.properties.providerSettings; - const userSettingsUri = this.getConsoleUserSettingsUri(metadata.settings.armResource.endpoint); - const userSettingsResult = await axios.get(userSettingsUri, settings); + + let userSettingsResult: AxiosResponse; + try { + userSettingsResult = await axios.get(userSettingsUri, settings); + } catch (ex) { + console.log(ex, ex.response); + await handleNeverUsed(); + return; + } const preferredShell = userSettingsResult.data?.properties?.preferredShellType ?? 'bash'; const preferredLocation = userSettingsResult.data?.properties?.preferredLocation; @@ -43,7 +79,14 @@ export class AzureTerminalService implements IAzureTerminalService { settings.headers['x-ms-console-preferred-location'] = preferredLocation; } - const provisionResult = await axios.put(consoleRequestUri, {}, settings); + let provisionResult: AxiosResponse; + try { + provisionResult = await axios.put(consoleRequestUri, {}, settings); + } catch (ex) { + console.log(ex, ex.response); + await handleNeverUsed(); + return; + } if (provisionResult.data?.properties?.provisioningState !== 'Succeeded') { throw new Error(provisionResult.data); @@ -114,7 +157,7 @@ class AzureTerminal implements vscode.Pseudoterminal { } async open(initialDimensions: vscode.TerminalDimensions): Promise { - return this.resetTerminalSize(initialDimensions); + this.setDimensions(initialDimensions); } close(): void { @@ -131,24 +174,22 @@ class AzureTerminal implements vscode.Pseudoterminal { } async setDimensions(dimensions: vscode.TerminalDimensions): Promise { - return this.resetTerminalSize(dimensions); + if (!dimensions) { + return; + } + this.terminalDimensions = dimensions; + return this.resetTerminalSize(); } - private async resetTerminalSize(dimensions: vscode.TerminalDimensions): Promise { + private async resetTerminalSize(): Promise { try { - - if (!this.terminalDimensions) { // first time - this.writeEmitter.fire(localize('azure.connectingShellTerminal', "Connecting terminal...\n")); - } - - if (dimensions) { - this.terminalDimensions = dimensions; - } - // Close the shell before this and restablish a new connection this.close(); const terminalUri = await this.establishTerminal(this.terminalDimensions); + if (!terminalUri) { + return; + } this.socket = new WS(terminalUri); this.socket.on('message', (data: WS.Data) => { @@ -172,13 +213,20 @@ class AzureTerminal implements vscode.Pseudoterminal { private async establishTerminal(dimensions: vscode.TerminalDimensions): Promise { - const terminalResult = await axios.post(`${this.consoleUri}/terminals?rows=${dimensions.rows}&cols=${dimensions.columns}&shell=${this.shell}`, undefined, { - headers: { - 'Accept': 'application/json', - 'Content-Type': 'application/json', - 'Authorization': `Bearer ${this.token}` - } - }); + let terminalResult: AxiosResponse; + try { + terminalResult = await axios.post(`${this.consoleUri}/terminals?rows=${dimensions.rows}&cols=${dimensions.columns}&shell=${this.shell}`, undefined, { + headers: { + 'Accept': 'application/json', + 'Content-Type': 'application/json', + 'Authorization': `Bearer ${this.token}` + } + }); + } catch (ex) { + console.log(ex, ex.response); + await handleNeverUsed(); + return undefined; + } const terminalUri = terminalResult.data?.socketUri;