diff --git a/src/VSCodeExtension/package-lock.json.v.template b/src/VSCodeExtension/package-lock.json.v.template index f78a43b61c..0f62cedd95 100644 --- a/src/VSCodeExtension/package-lock.json.v.template +++ b/src/VSCodeExtension/package-lock.json.v.template @@ -19,6 +19,7 @@ "decompress-zip": "^0.2.2", "dotnet": "^1.1.4", "edge-js": "^18.4.0", + "find-open-port": "^2.0.3", "fs-extra": "^8.1.0", "g": "^2.0.1", "install": "^0.13.0", @@ -2473,6 +2474,14 @@ "node": ">=8" } }, + "node_modules/find-open-port": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/find-open-port/-/find-open-port-2.0.3.tgz", + "integrity": "sha512-lcYIpPHWNksWSSgUxe+gAq8LqG7wCpkhohhTVG0E0c5KT1Lrpb5cY7bJ+fO49Dax1TkAw5paKAoBSTgYwnBKrA==", + "engines": { + "node": ">=10.13" + } + }, "node_modules/find-up": { "version": "5.0.0", "license": "MIT", @@ -8393,6 +8402,11 @@ "to-regex-range": "^5.0.1" } }, + "find-open-port": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/find-open-port/-/find-open-port-2.0.3.tgz", + "integrity": "sha512-lcYIpPHWNksWSSgUxe+gAq8LqG7wCpkhohhTVG0E0c5KT1Lrpb5cY7bJ+fO49Dax1TkAw5paKAoBSTgYwnBKrA==" + }, "find-up": { "version": "5.0.0", "requires": { diff --git a/src/VSCodeExtension/package.json.v.template b/src/VSCodeExtension/package.json.v.template index 47dfbc94f3..85f7de93e9 100644 --- a/src/VSCodeExtension/package.json.v.template +++ b/src/VSCodeExtension/package.json.v.template @@ -31,7 +31,6 @@ "onCommand:quantum.openDocumentation", "onCommand:quantum.installIQSharp", "onCommand:quantum.connectToAzureAccount", - "onCommand:quantum.disconnectFromAzureAccount", "onCommand:quantum.changeWorkspace", "onCommand:quantum.submitJob", "onCommand:quantum.getJob", @@ -61,10 +60,6 @@ "command": "quantum.connectToAzureAccount", "title": "Q#: Connect to Azure Account" }, - { - "command": "quantum.disconnectFromAzureAccount", - "title": "Q#: Disconnect from Azure Account" - }, { "command": "quantum.changeWorkspace", "title": "Q#: Change Workspace" @@ -161,6 +156,16 @@ ] }, "menus": { + "commandPalette": [ + { + "command": "quantum.changeAzureAccount", + "when": "showChangeAzureAccount == true" + }, + { + "command": "quantum.connectToAzureAccount", + "when": "showChangeAzureAccount == false" + } + ], "view/title": [ { "command": "quantum-jobs.clearJobs", @@ -198,6 +203,7 @@ "decompress-zip": "^0.2.2", "dotnet": "^1.1.4", "edge-js": "^18.4.0", + "find-open-port": "^2.0.3", "fs-extra": "^8.1.0", "g": "^2.0.1", "install": "^0.13.0", diff --git a/src/VSCodeExtension/src/commands.ts b/src/VSCodeExtension/src/commands.ts index 9759d409e3..60d554f242 100644 --- a/src/VSCodeExtension/src/commands.ts +++ b/src/VSCodeExtension/src/commands.ts @@ -1,6 +1,6 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT License. - + "use strict"; import * as vscode from "vscode"; import * as cp from "child_process"; @@ -216,12 +216,12 @@ export async function getAzureQuantumConfig():Promise { } -async function getWorkspaceInfo(context: vscode.ExtensionContext, credential:InteractiveBrowserCredential | AzureCliCredential, workSpaceStatusBarItem: vscode.StatusBarItem, totalSteps:number){ +export async function getWorkspaceInfo(context: vscode.ExtensionContext, credential:InteractiveBrowserCredential | AzureCliCredential, workspaceStatusBarItem: vscode.StatusBarItem, totalSteps:number){ let workspaceInfo:workspaceInfo|undefined; // remove any previous account info context.workspaceState.update("workspaceInfo", undefined); try{ - await getWorkspaceFromUser(context, credential, workSpaceStatusBarItem, totalSteps); + await getWorkspaceFromUser(context, credential, workspaceStatusBarItem, totalSteps); workspaceInfo = context.workspaceState.get("workspaceInfo"); } catch{ @@ -236,13 +236,6 @@ async function getWorkspaceInfo(context: vscode.ExtensionContext, credential:Int } - -export async function deleteAzureWorkspaceInfo(context: vscode.ExtensionContext) { - context.workspaceState.update("workspaceInfo", undefined); - vscode.window.showInformationMessage("Successfully Disconnected"); -} - - function getQuantumJobClient(workspaceInfo: workspaceInfo, credential: any) { const endpoint = "https://" + workspaceInfo["location"] + ".quantum.azure.com"; return new QuantumJobClient( @@ -263,13 +256,13 @@ export async function submitJob( dotNetSdk: DotnetInfo, jobsSubmittedProvider: LocalSubmissionsProvider, credential: InteractiveBrowserCredential | AzureCliCredential, - workSpaceStatusBarItem: vscode.StatusBarItem, + workspaceStatusBarItem: vscode.StatusBarItem, workspaceInfo:workspaceInfo|undefined, projectFiles: any[], totalSteps: number ) { if (!workspaceInfo){ - workspaceInfo = await getWorkspaceInfo(context, credential,workSpaceStatusBarItem, totalSteps); + workspaceInfo = await getWorkspaceInfo(context, credential,workspaceStatusBarItem, totalSteps); } if (!workspaceInfo){ return; @@ -425,10 +418,6 @@ async function getJob(context: vscode.ExtensionContext, { let jobResponse:any; - let cachedJobs = context.workspaceState.get("cachedJobs") as any; - if (cachedJobs && cachedJobs[jobId]) { - return cachedJobs[jobId]; - } await vscode.window.withProgress( { location: vscode.ProgressLocation.Notification, @@ -437,10 +426,6 @@ async function getJob(context: vscode.ExtensionContext, }, async (progress, token2) => { - if (!cachedJobs) { - cachedJobs = {}; - } - const quantumJobClient = getQuantumJobClient(workspaceInfo, credential); @@ -459,12 +444,6 @@ async function getJob(context: vscode.ExtensionContext, } return; } - - // if job suceeded store output - if(jobResponse.status ==="Succeeded"){ - cachedJobs[jobId]= jobResponse; - context.workspaceState.update("cachedJobs", cachedJobs); - } } ); return jobResponse; @@ -476,7 +455,7 @@ async function getJob(context: vscode.ExtensionContext, export async function getJobResults( context: vscode.ExtensionContext, credential: InteractiveBrowserCredential | AzureCliCredential, - workSpaceStatusBarItem: vscode.StatusBarItem, + workspaceStatusBarItem: vscode.StatusBarItem, jobId?: string ) { @@ -488,7 +467,7 @@ export async function getJobResults( // if user needs to select a workspace, show steps in title const inputTitle = workspaceInfo?"Enter a Job Id": "Enter Job Id (4/4)"; if(configIssue){ - workspaceInfo = await getWorkspaceInfo(context, credential,workSpaceStatusBarItem, 4); + workspaceInfo = await getWorkspaceInfo(context, credential,workspaceStatusBarItem, 4); } if(!workspaceInfo){ return; @@ -552,6 +531,16 @@ export async function getJobResults( } openReadOnlyJson({ label: `results-${job.name}`, fullId: jobId as string}, refinedJson); + } + else if(outputString.includes("Error")) + { + try{ + const message = outputString.split("")[1].split("")[0]; + vscode.window.showErrorMessage(message); + } + catch{ + vscode.window.showErrorMessage("Error retrieving job."); + } } }).on("error", function (err) { vscode.window.showErrorMessage(err.message); @@ -563,15 +552,15 @@ export async function getJobResults( export async function getJobDetails( context: vscode.ExtensionContext, credential: InteractiveBrowserCredential | AzureCliCredential, - jobId: string, - workSpaceStatusBarItem: vscode.StatusBarItem + workspaceStatusBarItem: vscode.StatusBarItem, + jobId: string ) { let {workspaceInfo, configIssue} = await getAzureQuantumConfig(); if(configIssue===configIssueEnum.MULTIPLE_CONFIGS){ return; } if(configIssue){ - workspaceInfo = await getWorkspaceInfo(context, credential,workSpaceStatusBarItem, 5); + workspaceInfo = await getWorkspaceInfo(context, credential,workspaceStatusBarItem, 5); } if(!workspaceInfo){ return; diff --git a/src/VSCodeExtension/src/extension.ts b/src/VSCodeExtension/src/extension.ts index 6a87a93935..d23849790e 100644 --- a/src/VSCodeExtension/src/extension.ts +++ b/src/VSCodeExtension/src/extension.ts @@ -12,16 +12,23 @@ import { installTemplates, createNewProject, registerCommand, openDocumentationH import { LanguageServer } from './languageServer'; import {LocalSubmissionsProvider} from './localSubmissionsProvider'; import {registerUIExtensionVariables, createAzExtOutputChannel, UIExtensionVariables } from '@microsoft/vscode-azext-utils'; -import { AzureCliCredential, InteractiveBrowserCredential, AccessToken } from '@azure/identity'; +import { AzureCliCredential, InteractiveBrowserCredential, ChainedTokenCredential } from '@azure/identity'; import {getWorkspaceFromUser} from "./quickPickWorkspace"; -import {workspaceInfo, getAzureQuantumConfig, configIssueEnum} from "./commands"; +import {getAzureQuantumConfig, configIssueEnum, getWorkspaceInfo} from "./commands"; +import { AbortController} from "@azure/abort-controller"; +import * as https from "https"; +const findPort = require('find-open-port'); let credential:AzureCliCredential|InteractiveBrowserCredential; -let authSource: string; -let accountAuthStatusBarItem: vscode.StatusBarItem; -let workSpaceStatusBarItem: vscode.StatusBarItem; +let workspaceStatusBarItem: vscode.StatusBarItem; let submitJobStatusBarItem: vscode.StatusBarItem; +// Flag to prevent user from launching multiple login flows +let currentlyAuthenticating = false; +// MSAL treats the Microsoft account system (Live, MSA) as another tenant +// within the Microsoft identity platform. The tenant id of the Microsoft +// account tenant is 9188040d-6c67-4c5b-b112-36a304b66dad +const MSA_ACCOUNT_TENANT = "9188040d-6c67-4c5b-b112-36a304b66dad"; /** * Returns the root folder for the current workspace. */ @@ -35,40 +42,188 @@ function findRootFolder() : string { } } -// required before any interaction with Azure -// try to authenticate though AzureCliCredential first. If fails, authenticate through InteractiveBrowserCredential -async function getCredential(context: vscode.ExtensionContext, changeAccount=false ){ - let token:AccessToken; - if (credential && !changeAccount){ - return credential; +// handles cancellation of ports +async function abort(port:number, controller:AbortController){ + if (await findPort.isAvailable(port)){ + setTimeout(abort,100, port, controller); + } else { + controller.abort(); } + } + + + +async function getCredential(context: vscode.ExtensionContext, changeAccountFlag=false ){ + return new Promise(async(resolve, reject)=>{ + + // For the general case where a user has already generated a credential + // and is not changing accounts + if (credential && !changeAccountFlag){ + return resolve(); + } + + // prevents user from having multiple authorization requests at the same time + if(currentlyAuthenticating){ + return reject(); + } + + + currentlyAuthenticating=true; + const portOne = await findPort.findPort(); + let tempCredentialOne = new ChainedTokenCredential(new AzureCliCredential(), new InteractiveBrowserCredential({"redirectUri":`http://localhost:${portOne}`})); + // Flag for triggering MSA authentication flow + let authenticationMSAUser= false; + // Below variables only needed for MSA user login + let tempCredentialTwo: InteractiveBrowserCredential | undefined; + let tenantJSON:any; + let tenantId:string; + + await vscode.window.withProgress({ location: vscode.ProgressLocation.Notification, title: "Authenticating...", - }, async (progress, token2) => { - let tempCredential:any; + "cancellable":true + }, async (progress, cancel) => { try{ - tempCredential = new AzureCliCredential(); - // if a user is changing their account always trigger InteractiveBrowserCredential - if(changeAccount){ - // tslint:disable-next-line:no-unused-expression - (token as any)["THROWERROR"]; + const controllerOne = new AbortController(); + const abortSignalOne = controllerOne.signal; + cancel.onCancellationRequested(async (e:any)=>{ + // abort the request, closing the tls port connection + // otherwise the port could stay connected until the program + // is terminated + await abort(portOne, controllerOne); + currentlyAuthenticating = false; + return reject(); + + }); + const token = await tempCredentialOne.getToken("https://management.azure.com/.default", {"abortSignal":abortSignalOne}); + + //@ts-ignore + authenticationMSAUser = !!(tempCredentialOne?._sources[1]?.msalFlow?.account?.homeAccountId.includes(MSA_ACCOUNT_TENANT)); + + // Pull tenants ONLY for MSA users + if(authenticationMSAUser && currentlyAuthenticating){ + const options:any = { + headers: { + Authorization: `Bearer ${token.token}`, + + }, + resolveWithFullResponse:true + }; + tenantJSON = await new Promise((resolve, reject)=>{ + + //@ts-ignore + const req = https.get("https://management.azure.com/tenants?api-version=2020-01-01", options, (res:any) => { + let responseBody = ''; + + res.on('data', (chunk:any) => { + responseBody += chunk; + }); + + res.on('end', () => { + resolve(JSON.parse(responseBody)); + }); + }); + + req.on('error', (err) => { + reject(err); + }); + }); + + } } - // need to call getToken to validate if user is logged in through Az CLI - token = await tempCredential.getToken(["https://management.azure.com/.default","https://quantum.microsoft.com/.default"]); - authSource = "Az CLI"; - } - catch{ - // login through browser if user is not logged in through Az CLI - tempCredential = new InteractiveBrowserCredential(); - token = await tempCredential.getToken("https://management.azure.com/.default"); - authSource = "browser"; - } - accountAuthStatusBarItem.tooltip = `Authenticated from ${authSource}`; - accountAuthStatusBarItem.show(); - credential = tempCredential; + catch(err:any){ + if(err && err.message === "Aborted"){ + vscode.window.showErrorMessage("Unable to authenticate."); + } + currentlyAuthenticating=false; + return reject(); + } + + }); + // handles tenant selection for MSA users + if(authenticationMSAUser && currentlyAuthenticating){ + if(tenantJSON.value.length === 0){ + currentlyAuthenticating=false; + vscode.window.showErrorMessage("No tenants available."); + return reject(); + } + else if (tenantJSON.value.length === 1){ + tenantId = tenantJSON.value[0]["tenantId"]; + + } + else{ + const tenantObj:any = await vscode.window.showQuickPick(tenantJSON.value.map((tenant:any)=>{ + return { + label: tenant["displayName"], + description: tenant["tenantId"] + }; + }), + {"ignoreFocusOut":true, "matchOnDescription":true, "title":"Select a Tenant."}); + + if(!tenantObj){ + currentlyAuthenticating=false; + return reject(); + } + tenantId = tenantObj["description"]; + + } + if (!tenantId){ + currentlyAuthenticating=false; + return reject(); + } + + } + + // handles second authentication ONLY for MSA users + if(authenticationMSAUser && currentlyAuthenticating){ + await vscode.window.withProgress({ + location: vscode.ProgressLocation.Notification, + title: "Authenticating (there is a second browser login)...", + "cancellable":true + }, async (progress, cancel) => { + const controllerTwo = new AbortController(); + const abortSignalTwo = controllerTwo.signal; + try{ + const portTwo= await findPort.findPort(); + cancel.onCancellationRequested(async(e:any)=>{ + // abort the request, which opens the tls port for another authentication request + await abort(portTwo, controllerTwo); + currentlyAuthenticating = false; + return reject(); + }); + + tempCredentialTwo = new InteractiveBrowserCredential({"tenantId":tenantId, "redirectUri":`http://localhost:${portTwo}`}); + await tempCredentialTwo.getToken("https://management.azure.com/.default", {"abortSignal":abortSignalTwo}); + + // Verifies MSA user selected the same account in both authentication login pop-ups + //@ts-ignore + if(!!(tempCredentialOne?._sources[1]?.msalFlow?.account?.username !== tempCredentialTwo?.msalFlow?.account?.username)){ + throw Error("Aborted"); + } + } + catch(err:any){ + if(err && err.message === "Aborted"){ + vscode.window.showErrorMessage("Unable to authenticate."); + } + currentlyAuthenticating=false; + return reject(); + } + + }); + } + + if (!currentlyAuthenticating){ + return reject(); + } + + credential = tempCredentialTwo? tempCredentialTwo: tempCredentialOne; + vscode.commands.executeCommand('setContext', 'showChangeAzureAccount', true); + currentlyAuthenticating=false; + return resolve(); + }); - return; + } // this method is called when your extension is activated @@ -113,6 +268,8 @@ export async function activate(context: vscode.ExtensionContext) { vscode.window.createTreeView("quantum-jobs", { treeDataProvider: localSubmissionsProvider, }); + // shows "Connect to Azure Account" in command pallete, hides "Change Azure Account" from pallete + vscode.commands.executeCommand('setContext', 'showChangeAzureAccount', false); // need to call registerUIExtensionVariables to use openReadOnlyJson from // @microsoft/vscode-azext-utils package @@ -120,16 +277,7 @@ export async function activate(context: vscode.ExtensionContext) { const args: UIExtensionVariables = {context: context, outputChannel:AzExtOutputChannel}; registerUIExtensionVariables(args); - - accountAuthStatusBarItem = vscode.window.createStatusBarItem(vscode.StatusBarAlignment.Left, 202); - accountAuthStatusBarItem.command = "quantum.changeAzureAccount"; - context.subscriptions.push(accountAuthStatusBarItem); - accountAuthStatusBarItem.text = `$(verified)`; - - if(authSource){ - accountAuthStatusBarItem.tooltip = `Azure Quantum Auth: ${authSource}`; - accountAuthStatusBarItem.show(); - } + context.workspaceState.update("workspaceInfo", undefined); submitJobStatusBarItem = vscode.window.createStatusBarItem(vscode.StatusBarAlignment.Left, 201); submitJobStatusBarItem.command = "quantum.submitJob"; @@ -139,14 +287,15 @@ export async function activate(context: vscode.ExtensionContext) { - workSpaceStatusBarItem = vscode.window.createStatusBarItem(vscode.StatusBarAlignment.Left, 200); - const workspaceInfo: workspaceInfo | undefined = context.workspaceState.get("workspaceInfo"); - workSpaceStatusBarItem.command = "quantum.changeWorkspace"; - context.subscriptions.push(workSpaceStatusBarItem); - if (workspaceInfo && workspaceInfo["workspace"]){ - workSpaceStatusBarItem.text = `Azure Workspace: ${workspaceInfo["workspace"]}`; - workSpaceStatusBarItem.show(); + workspaceStatusBarItem = vscode.window.createStatusBarItem(vscode.StatusBarAlignment.Left, 200); + if(!credential){ + + workspaceStatusBarItem.text = "Connect to Azure Workspace"; + workspaceStatusBarItem.command = "quantum.getWorkspace"; + workspaceStatusBarItem.show(); } + context.subscriptions.push(workspaceStatusBarItem); + // Register commands that use the .NET Core SDK. // We do so as early as possible so that we can handle if someone calls @@ -200,15 +349,14 @@ export async function activate(context: vscode.ExtensionContext) { } return false; } - - } registerCommand( context, "quantum.submitJob", async() => { - await getCredential(context); + await getCredential(context).then( async()=>{ + sendTelemetryEvent(EventNames.jobSubmissionStarted, {},{}); // base amount of steps (provider, target, jobName, programArguments) let totalSteps = 4; @@ -233,39 +381,76 @@ export async function activate(context: vscode.ExtensionContext) { totalSteps = multipleProjFilesFlag?totalSteps+1:totalSteps; requireDotNetSdk(dotNetSdkVersion).then( - dotNetSdk => submitJob(context, dotNetSdk, localSubmissionsProvider, credential, workSpaceStatusBarItem, workspaceInfo, projectFiles, totalSteps) + dotNetSdk => submitJob(context, dotNetSdk, localSubmissionsProvider, credential, workspaceStatusBarItem, workspaceInfo, projectFiles, totalSteps) ); + }).catch((err)=>{ + if (err){ + console.log(err); + } + }); } ); - // registerCommand( - // context, - // "quantum.disconnectFromAzureAccount", - // () => { - // deleteAzureWorkspaceInfo(context); - // } - // ); registerCommand( context, "quantum.changeWorkspace", async() => { - sendTelemetryEvent(EventNames.changeWorkspace, {},{}); - await getCredential(context); + await getCredential(context).then(async()=>{ // three total steps - await getWorkspaceFromUser(context, credential, workSpaceStatusBarItem, 3); + await getWorkspaceFromUser(context, credential, workspaceStatusBarItem, 3); + sendTelemetryEvent(EventNames.changeWorkspace, {},{}); + }).catch((err)=>{ + if (err){ + console.log(err); + } + }); } ); + registerCommand( + context, + "quantum.getWorkspace", + async() => { + await getCredential(context).then(async()=>{ + let {workspaceInfo, configIssue} = await getAzureQuantumConfig(); + if(configIssue===configIssueEnum.MULTIPLE_CONFIGS){ + return; + } + else if(configIssue){ + workspaceInfo = await getWorkspaceInfo(context, credential,workspaceStatusBarItem, 3); + } + if(!workspaceInfo){ + return; + } + if (workspaceInfo["workspace"]){ + workspaceStatusBarItem.text = `Azure Workspace: ${workspaceInfo["workspace"]}`; + workspaceStatusBarItem.show(); + workspaceStatusBarItem.command ="quantum.changeWorkspace"; + } + + }).catch((err)=>{ + if (err){ + console.log(err); + } + }); + + } + ); registerCommand( context, "quantum.getJob", async() => { - sendTelemetryEvent(EventNames.getJobResults, {"method": "command line"},{}); - await getCredential(context); - getJobResults(context, credential,workSpaceStatusBarItem); + await getCredential(context).then(async()=>{ + await getJobResults(context, credential,workspaceStatusBarItem); + sendTelemetryEvent(EventNames.getJobResults, {"method": "command line"},{}); + }).catch((err)=>{ + if (err){ + console.log(err); + } + }); } ); @@ -280,28 +465,49 @@ export async function activate(context: vscode.ExtensionContext) { ); vscode.commands.registerCommand('quantum-jobs.jobDetails', async (job) =>{ - sendTelemetryEvent(EventNames.getJobDetails, {},{}); - await getCredential(context); - const jobId = job['jobDetails']['jobId']; - getJobDetails(context, credential, jobId, workSpaceStatusBarItem); + await getCredential(context).then(async()=>{ + const jobId = job['jobDetails']['jobId']; + await getJobDetails(context, credential,workspaceStatusBarItem, jobId); + sendTelemetryEvent(EventNames.getJobDetails, {},{}); + }).catch((err)=>{ + if (err){ + console.log(err); + } + }); }); vscode.commands.registerCommand('quantum-jobs.jobResults', async (job) =>{ - await getCredential(context); - const jobId = job['jobDetails']['jobId']; - sendTelemetryEvent( - EventNames.results, - { - 'method': "Results button" - }, - ); - getJobResults(context, credential,workSpaceStatusBarItem, jobId); + await getCredential(context).then(async()=>{ + sendTelemetryEvent( + EventNames.results, + { + 'method': "Results button" + }, + ); + const jobId = job['jobDetails']['jobId']; + await getJobResults(context, credential,workspaceStatusBarItem, jobId); + }).catch((err)=>{ + if (err){ + console.log(err); + } + }); } ); + vscode.commands.registerCommand('quantum.connectToAzureAccount', async () =>{ + sendTelemetryEvent(EventNames.connectToAzureAccount, {},{}); + await getCredential(context); + }); + vscode.commands.registerCommand('quantum.changeAzureAccount', async () =>{ - sendTelemetryEvent(EventNames.changeAzureAccount, {},{}); - await getCredential(context, true); + await getCredential(context, true).then(async()=>{ + sendTelemetryEvent(EventNames.changeAzureAccount, {},{}); + context.workspaceState.update("workspaceInfo", undefined); + }).catch((err)=>{ + if (err){ + console.log(err); + } + }); }); @@ -335,6 +541,7 @@ export async function activate(context: vscode.ExtensionContext) { // this method is called when your extension is deactivated export function deactivate() { + currentlyAuthenticating = false; if (reporter) { reporter.dispose(); } } diff --git a/src/VSCodeExtension/src/quickPickJob.ts b/src/VSCodeExtension/src/quickPickJob.ts index 47e4c23c15..3d41e6fac3 100644 --- a/src/VSCodeExtension/src/quickPickJob.ts +++ b/src/VSCodeExtension/src/quickPickJob.ts @@ -31,6 +31,13 @@ let csprojQuickPickFlag=false; export async function getJobInfoFromUser(context:vscode.ExtensionContext, quantumJobClient:QuantumJobClient, workspaceInfo:workspaceInfo, totalSteps:number, projectFiles:any[]){ + provider=""; + _target=""; + jobName=""; + csproj=""; + programArguments=""; + providersAndTargets=""; + submissionStepEnum = { CSPROJ: totalSteps - 4, PROVIDER:totalSteps - 3, @@ -154,6 +161,7 @@ export async function getJobInfoFromUser(context:vscode.ExtensionContext, quantu function setupCsProjQuickPick(quickPick:vscode.QuickPick, projectFiles:any[]){ quickPick.buttons = []; + provider=""; quickPick.step = submissionStepEnum.CSPROJ; quickPick.matchOnDetail = true; quickPick.value = csproj?csproj:""; @@ -206,7 +214,7 @@ async function setupProviderQuickPick(quickPick:vscode.QuickPick { if ( currentworkspaceInfo && @@ -180,6 +186,7 @@ export async function getWorkspaceFromUser( } return { label: rg.name }; }); + } quickPick.enabled = true; quickPick.busy = false; quickPick.show(); @@ -191,6 +198,7 @@ export async function getWorkspaceFromUser( currentworkspaceInfo: workspaceInfo | unknown, options: any ) { + quickPick.placeholder = ""; quickPick.items = []; quickPick.step = selectionStepEnum.SUBSCRIPTION; quickPick.title = "Select Subscription"; @@ -221,6 +229,10 @@ export async function getWorkspaceFromUser( }); if (quickPick.step===selectionStepEnum.SUBSCRIPTION){ + if(subscriptionsJSON.value.length ===0){ + quickPick.placeholder = "No subscriptions found."; + } + else{ quickPick.items = subscriptionsJSON.value.map((subscription: any) => { if ( currentworkspaceInfo && @@ -234,6 +246,7 @@ export async function getWorkspaceFromUser( description: subscription.subscriptionId, }; }); + } } quickPick.enabled = true; quickPick.busy = false; @@ -249,6 +262,7 @@ export async function getWorkspaceFromUser( quickPick.buttons = [vscode.QuickInputButtons.Back]; quickPick.enabled = false; quickPick.busy = true; + quickPick.placeholder = ""; quickPick.show(); const workspacesJSON:any = await new Promise((resolve, reject)=>{ @@ -277,6 +291,9 @@ export async function getWorkspaceFromUser( return false; }); if (quickPick.step===selectionStepEnum.WORKSPACE){ + if(quantumWorkspaces.length===0){ + quickPick.placeholder = "No Quantum Workspaces found."; + } quickPick.items = quantumWorkspaces.map((workspace: any) => { return { label: workspace.name, diff --git a/src/VSCodeExtension/src/telemetry.ts b/src/VSCodeExtension/src/telemetry.ts index 07afff2cf6..9c3322115e 100644 --- a/src/VSCodeExtension/src/telemetry.ts +++ b/src/VSCodeExtension/src/telemetry.ts @@ -35,6 +35,7 @@ export namespace EventNames { export const getJobResults = "get-job-results"; export const getJobDetails = "get-job-details"; export const changeAzureAccount = "change-azure-account"; + export const connectToAzureAccount = "connect-to--azure-account"; } // @ts-ignore