import * as escape from 'escape-string-regexp' ; import { AzureWizardExecuteStep , AzureWizardPromptStep , IActionContext , IAzureQuickPickItem , IAzureQuickPickOptions , IWizardOptions } from 'vscode-azureextensionui' ; import { ProjectLanguage , TemplateFilter , templateFilterSetting } from '../../constants' ; import { canValidateAzureWebJobStorageOnDebug } from '../../debug/validatePreDebug' ; import { ext } from '../../extensionVariables' ; import { getAzureWebJobsStorage } from '../../funcConfig/local.settings' ; import { FuncVersion } from '../../FuncVersion' ; import { localize } from '../../localize' ; import { IFunctionTemplate } from '../../templates/IFunctionTemplate' ; import { nonNullProp } from '../../utils/nonNull' ; import { getWorkspaceSetting , updateWorkspaceSetting } from '../../vsCodeConfig/settings' ; import { addBindingSettingSteps } from '../addBinding/settingSteps/addBindingSettingSteps' ; import { AzureWebJobsStorageExecuteStep } from '../appSettings/AzureWebJobsStorageExecuteStep' ; import { AzureWebJobsStoragePromptStep } from '../appSettings/AzureWebJobsStoragePromptStep' ; import { JavaPackageNameStep } from '../createNewProject/javaSteps/JavaPackageNameStep' ; import { DotnetFunctionCreateStep } from './dotnetSteps/DotnetFunctionCreateStep' ; import { DotnetFunctionNameStep } from './dotnetSteps/DotnetFunctionNameStep' ; import { DotnetNamespaceStep } from './dotnetSteps/DotnetNamespaceStep' ; import { IFunctionWizardContext } from './IFunctionWizardContext' ; import { JavaFunctionCreateStep } from './javaSteps/JavaFunctionCreateStep' ; import { JavaFunctionNameStep } from './javaSteps/JavaFunctionNameStep' ; import { OpenAPICreateStep } from './openAPISteps/OpenAPICreateStep' ; import { OpenAPIGetSpecificationFileStep } from './openAPISteps/OpenAPIGetSpecificationFileStep' ; import { ScriptFunctionCreateStep } from './scriptSteps/ScriptFunctionCreateStep' ; import { ScriptFunctionNameStep } from './scriptSteps/ScriptFunctionNameStep' ; import { TypeScriptFunctionCreateStep } from './scriptSteps/TypeScriptFunctionCreateStep' ; export class FunctionListStep extends AzureWizardPromptStep < IFunctionWizardContext > { public hideStepCount : boolean = true ; private readonly _functionSettings : { [ key ] : string | undefined ; } ; private readonly _isProjectWizard : boolean ; private constructor ( functionSettings , isProjectWizard ) { super ( ) ; this . _functionSettings = functionSettings || { } ; this . _isProjectWizard = ! ! isProjectWizard ; } public static async create ( context , options ) { if ( options . templateId ) { const language = nonNullProp ( context , 'language' ) ; const version = nonNullProp ( context , 'version' ) ; const templateProvider = ext . templateProvider . get ( context ) ; const templates = await templateProvider . getFunctionTemplates ( context , context . projectPath , language , version , TemplateFilter . All , context . projectTemplateKey ) ; const foundTemplate = templates . find ( ( t ) => { if ( options . templateId ) { const actualId = t . id . toLowerCase ( ) ; const expectedId = options . templateId . toLowerCase ( ) ; return actualId === expectedId || new RegExp ( `\\b${ escape ( expectedId ) }\\b` , 'gmi' ) . test ( actualId ) ; } else { return false ; } } ) ; if ( foundTemplate ) { context . functionTemplate = foundTemplate ; } else { throw new Error ( localize ( 'templateNotFound' , 'Could not find template with language "{0}", version "{1}", and id "{2}".' , context . language , context . version , options . templateId ) ) ; } } return new FunctionListStep ( options . functionSettings , options . isProjectWizard ) ; } public async getSubWizard ( context ) { const template = context . functionTemplate ; if ( template ) { const promptSteps = [ ] ; switch ( context . language ) { case ProjectLanguage . Java : promptSteps . push ( new JavaPackageNameStep ( ) , new JavaFunctionNameStep ( ) ) ; break ; case ProjectLanguage . CSharp : case ProjectLanguage . FSharp : promptSteps . push ( new DotnetFunctionNameStep ( ) , new DotnetNamespaceStep ( ) ) ; break ; default : promptSteps . push ( new ScriptFunctionNameStep ( ) ) ; break ; } for ( const key of Object . keys ( this . _functionSettings ) ) { context [ key . toLowerCase ( ) ] = this . _functionSettings [ key ] ; } addBindingSettingSteps ( template . userPromptedSettings , promptSteps ) ; const executeSteps = [ ] ; switch ( context . language ) { case ProjectLanguage . Java : executeSteps . push ( new JavaFunctionCreateStep ( ) ) ; break ; case ProjectLanguage . CSharp : case ProjectLanguage . FSharp : executeSteps . push ( await DotnetFunctionCreateStep . createStep ( context ) ) ; break ; case ProjectLanguage . TypeScript : executeSteps . push ( new TypeScriptFunctionCreateStep ( ) ) ; break ; default : executeSteps . push ( new ScriptFunctionCreateStep ( ) ) ; break ; } if ( ! template . isHttpTrigger && ! canValidateAzureWebJobStorageOnDebug ( context . language ) && ! await getAzureWebJobsStorage ( context , context . projectPath ) ) { promptSteps . push ( new AzureWebJobsStoragePromptStep ( ) ) ; executeSteps . push ( new AzureWebJobsStorageExecuteStep ( ) ) ; } const title = localize ( 'createFunction' , 'Create new {0}' , template . name ) ; return { promptSteps , executeSteps , title } ; } else if ( context . generateFromOpenAPI ) { const promptSteps = [ ] ; const executeSteps = [ ] ; switch ( context . language ) { case ProjectLanguage . Java : promptSteps . push ( new JavaPackageNameStep ( ) ) ; break ; case ProjectLanguage . CSharp : promptSteps . push ( new DotnetNamespaceStep ( ) ) ; break ; default : break ; } promptSteps . push ( new OpenAPIGetSpecificationFileStep ( ) ) ; executeSteps . push ( await OpenAPICreateStep . createStep ( context ) ) ; const title = localize ( 'createFunction' , 'Create new {0}' , 'HTTP Triggers from OpenAPI (v2/v3) Specification File' ) ; return { promptSteps , executeSteps , title } ; } else { return undefined ; } } public async prompt ( context ) { let templateFilter = getWorkspaceSetting < TemplateFilter > ( templateFilterSetting , context . projectPath ) || TemplateFilter . Verified ; const templateProvider = ext . templateProvider . get ( context ) ; while ( ! context . functionTemplate ) { let placeHolder = this . _isProjectWizard ? localize ( 'selectFirstFuncTemplate' , "Select a template for your project's first function" ) : localize ( 'selectFuncTemplate' , 'Select a template for your function' ) ; if ( templateProvider . templateSource ) { placeHolder += localize ( 'templateSource' , ' (Template source: "{0}")' , templateProvider . templateSource ) ; } const result = ( await context . ui . showQuickPick ( this . getPicks ( context , templateFilter ) , { placeHolder } ) ) . data ; if ( result === 'skipForNow' ) { context . telemetry . properties . templateId = 'skipForNow' ; break ; } else if ( result === 'changeFilter' ) { templateFilter = await promptForTemplateFilter ( context ) ; if ( ! this . _isProjectWizard || context . openBehavior === 'AlreadyOpen' ) { await updateWorkspaceSetting ( templateFilterSetting , templateFilter , context . projectPath ) ; } context . telemetry . properties . changedFilter = 'true' ; } else if ( result === 'openAPI' ) { context . generateFromOpenAPI = true ; break ; } else if ( result === 'reloadTemplates' ) { await templateProvider . clearTemplateCache ( context , context . projectPath , nonNullProp ( context , 'language' ) , nonNullProp ( context , 'version' ) ) ; context . telemetry . properties . reloaded = 'true' ; } else { context . functionTemplate = result ; } } context . telemetry . properties . templateFilter = templateFilter ; } public shouldPrompt ( context ) { return ! context . functionTemplate ; } private async getPicks ( context , templateFilter ) { const language = nonNullProp ( context , 'language' ) ; const version = nonNullProp ( context , 'version' ) ; const templateProvider = ext . templateProvider . get ( context ) ; const templates = await templateProvider . getFunctionTemplates ( context , context . projectPath , language , version , templateFilter , context . projectTemplateKey ) ; context . telemetry . measurements . templateCount = templates . length ; const picks = templates . sort ( ( a , b ) => sortTemplates ( a , b , templateFilter ) ) . map ( t => { return { label : t . name , data : t } ; } ) ; if ( this . _isProjectWizard ) { picks . unshift ( { label : localize ( 'skipForNow' , '$(clock) Skip for now' ) , data : 'skipForNow' , suppressPersistence : true } ) ; } if ( templates . length === 0 ) { picks . push ( { label : localize ( 'noTemplates' , '$(warning) No templates found' ) , suppressPersistence : true , data : < IFunctionTemplate | TemplatePromptResult > < unknown > undefined , onPicked : ( ) => { } } ) ; } else if ( language === ProjectLanguage . CSharp || language === ProjectLanguage . Java || language === ProjectLanguage . Python || language === ProjectLanguage . TypeScript ) { picks . push ( { label : localize ( 'openAPI' , 'HTTP trigger(s) from OpenAPI V2/V3 Specification (Preview)' ) , data : 'openAPI' , suppressPersistence : true } ) ; } picks . push ( { label : localize ( 'selectFilter' , '$(gear) Change template filter' ) , description : localize ( 'currentFilter' , 'Current: {0}' , templateFilter ) , data : 'changeFilter' , suppressPersistence : true } ) ; if ( getWorkspaceSetting < boolean > ( 'showReloadTemplates' ) ) { picks . push ( { label : localize ( 'reloadTemplates' , '$(sync) Reload templates' ) , data : 'reloadTemplates' , suppressPersistence : true } ) ; } return picks ; } } interface IFunctionListStepOptions { isProjectWizard : boolean ; templateId : string | undefined ; functionSettings : { [ key ] : string | undefined ; } | undefined ; } type TemplatePromptResult = 'changeFilter' | 'skipForNow' | 'openAPI' | 'reloadTemplates' ; async function promptForTemplateFilter ( context ) { const picks = [ { label : TemplateFilter . Verified , description : localize ( 'verifiedDescription' , '(Subset of "Core" that has been verified in VS Code)' ) , data : TemplateFilter . Verified } , { label : TemplateFilter . Core , data : TemplateFilter . Core } , { label : TemplateFilter . All , data : TemplateFilter . All } ] ; const options = { suppressPersistence : true , placeHolder : localize ( 'selectFilter' , 'Select a template filter' ) } ; return ( await context . ui . showQuickPick ( picks , options ) ) . data ; } /**
 * If templateFilter is verified, puts HttpTrigger/TimerTrigger at the top since they're the most popular
 * Otherwise sort alphabetically
 */ function sortTemplates ( a , b , templateFilter ) { if ( templateFilter === TemplateFilter . Verified ) { function getPriority ( id ) { if ( /\bhttptrigger\b/i . test ( id ) ) { return 1 ; } else if ( /\bhttptrigger/i . test ( id ) ) { return 2 ; } else if ( /\btimertrigger\b/i . test ( id ) ) { return 3 ; } else { return 4 ; } } return getPriority ( a . id ) - getPriority ( b . id ) ; } return a . name . localeCompare ( b . name ) ; }