import { BlobServiceClient } from '@azure/storage-blob' ; import * as fse from 'fs-extra' ; import * as path from 'path' ; import * as vscode from 'vscode' ; import { AzureWizard , IActionContext , parseError } from "vscode-azureextensionui" ; import { AzureWebJobsStorageExecuteStep } from "../commands/appSettings/AzureWebJobsStorageExecuteStep" ; import { AzureWebJobsStoragePromptStep } from "../commands/appSettings/AzureWebJobsStoragePromptStep" ; import { IAzureWebJobsStorageWizardContext } from "../commands/appSettings/IAzureWebJobsStorageWizardContext" ; import { tryGetFunctionProjectRoot } from '../commands/createNewProject/verifyIsProject' ; import { functionJsonFileName , localEmulatorConnectionString , localSettingsFileName , ProjectLanguage , projectLanguageSetting , workerRuntimeKey } from "../constants" ; import { ParsedFunctionJson } from "../funcConfig/function" ; import { azureWebJobsStorageKey , getAzureWebJobsStorage , MismatchBehavior , setLocalAppSetting } from "../funcConfig/local.settings" ; import { validateFuncCoreToolsInstalled } from '../funcCoreTools/validateFuncCoreToolsInstalled' ; import { localize } from '../localize' ; import { getFunctionFolders } from "../tree/localProject/LocalFunctionsTreeItem" ; import { getDebugConfigs , isDebugConfigEqual } from '../vsCodeConfig/launch' ; import { getWorkspaceSetting , tryGetFunctionsWorkerRuntimeForProject } from "../vsCodeConfig/settings" ; export interface IPreDebugValidateResult { workspace : vscode . WorkspaceFolder ; shouldContinue : boolean ; } export async function preDebugValidate ( context , debugConfig ) { const workspace = getMatchingWorkspace ( debugConfig ) ; let shouldContinue ; context . telemetry . properties . debugType = debugConfig . type ; try { context . telemetry . properties . lastValidateStep = 'funcInstalled' ; const message = localize ( 'installFuncTools' , 'You must have the Azure Functions Core Tools installed to debug your local functions.' ) ; shouldContinue = await validateFuncCoreToolsInstalled ( context , message , workspace . uri . fsPath ) ; if ( shouldContinue ) { context . telemetry . properties . lastValidateStep = 'getProjectRoot' ; const projectPath = await tryGetFunctionProjectRoot ( context , workspace ) ; if ( projectPath ) { const projectLanguage = getWorkspaceSetting ( projectLanguageSetting , projectPath ) ; context . telemetry . properties . projectLanguage = projectLanguage ; context . telemetry . properties . lastValidateStep = 'workerRuntime' ; await validateWorkerRuntime ( context , projectLanguage , projectPath ) ; context . telemetry . properties . lastValidateStep = 'azureWebJobsStorage' ; await validateAzureWebJobsStorage ( context , projectLanguage , projectPath ) ; context . telemetry . properties . lastValidateStep = 'emulatorRunning' ; shouldContinue = await validateEmulatorIsRunning ( context , projectPath ) ; } } } catch ( error ) { const pe = parseError ( error ) ; if ( pe . isUserCancelledError ) { shouldContinue = false ; } else { shouldContinue = true ; context . telemetry . properties . preDebugValidateError = pe . message ; } } context . telemetry . properties . shouldContinue = String ( shouldContinue ) ; return { workspace , shouldContinue } ; } export function canValidateAzureWebJobStorageOnDebug ( projectLanguage ) { switch ( projectLanguage ) { case ProjectLanguage . CSharp : case ProjectLanguage . FSharp : case ProjectLanguage . Java : return false ; default : return true ; } } function getMatchingWorkspace ( debugConfig ) { if ( vscode . workspace . workspaceFolders ) { for ( const workspace of vscode . workspace . workspaceFolders ) { try { const configs = getDebugConfigs ( workspace ) ; if ( configs . some ( c => isDebugConfigEqual ( c , debugConfig ) ) ) { return workspace ; } } catch { } } } throw new Error ( localize ( 'noDebug' , 'Failed to find launch config matching name "{0}", request "{1}", and type "{2}".' , debugConfig . name , debugConfig . request , debugConfig . type ) ) ; } /**
 * Automatically add worker runtime setting since it's required to debug, but often gets deleted since it's stored in "local.settings.json" which isn't tracked in source control
 */ async function validateWorkerRuntime ( context , projectLanguage , projectPath ) { const runtime = await tryGetFunctionsWorkerRuntimeForProject ( context , projectLanguage , projectPath ) ; if ( runtime ) { await setLocalAppSetting ( context , projectPath , workerRuntimeKey , runtime , MismatchBehavior . DontChange ) ; } } async function validateAzureWebJobsStorage ( context , projectLanguage , projectPath ) { if ( canValidateAzureWebJobStorageOnDebug ( projectLanguage ) ) { const azureWebJobsStorage = await getAzureWebJobsStorage ( context , projectPath ) ; if ( ! azureWebJobsStorage ) { const functionFolders = await getFunctionFolders ( context , projectPath ) ; const functions = await Promise . all ( functionFolders . map ( async ( ff ) => { const functionJsonPath = path . join ( projectPath , ff , functionJsonFileName ) ; return new ParsedFunctionJson ( await fse . readJSON ( functionJsonPath ) ) ; } ) ) ; if ( functions . some ( f => ! f . isHttpTrigger ) ) { const wizardContext = Object . assign ( context , { projectPath } ) ; const wizard = new AzureWizard ( wizardContext , { promptSteps : [ new AzureWebJobsStoragePromptStep ( true ) ] , executeSteps : [ new AzureWebJobsStorageExecuteStep ( ) ] } ) ; await wizard . prompt ( ) ; await wizard . execute ( ) ; } } } } /**
 * If AzureWebJobsStorage is set, pings the emulator to make sure it's actually running
 */ async function validateEmulatorIsRunning ( context , projectPath ) { const azureWebJobsStorage = await getAzureWebJobsStorage ( context , projectPath ) ; if ( azureWebJobsStorage && azureWebJobsStorage . toLowerCase ( ) === localEmulatorConnectionString . toLowerCase ( ) ) { try { const client = BlobServiceClient . fromConnectionString ( azureWebJobsStorage , { retryOptions : { maxTries : 1 } } ) ; await client . getProperties ( ) ; } catch ( error ) { const message = localize ( 'failedToConnectEmulator' , 'Failed to verify "{0}" connection specified in "{1}". Is the local emulator installed and running?' , azureWebJobsStorageKey , localSettingsFileName ) ; const learnMoreLink = process . platform === 'win32' ? 'https://aka.ms/AA4ym56' : 'https://aka.ms/AA4yef8' ; const debugAnyway = { title : localize ( 'debugAnyway' , 'Debug anyway' ) } ; const result = await context . ui . showWarningMessage ( message , { learnMoreLink , modal : true , stepName : 'failedToConnectEmulator' } , debugAnyway ) ; return result === debugAnyway ; } } return true ; }