Skip to content

Commit 5204869

Browse files
authored
Revert "Group templates by filter rather than filtering it out (#4773)" (#4818)
1 parent fdf713a commit 5204869

File tree

10 files changed

+83
-52
lines changed

10 files changed

+83
-52
lines changed

package.json

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1083,7 +1083,6 @@
10831083
"title": "Azure Functions",
10841084
"properties": {
10851085
"azureFunctions.templateFilter": {
1086-
"deprecationMessage": "This setting is no longer used to filter templates, but is being left to explain what the filter enumerations mean.",
10871086
"scope": "resource",
10881087
"type": "string",
10891088
"default": "Verified",

src/commands/createFunction/FunctionListStep.ts

Lines changed: 58 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -3,18 +3,18 @@
33
* Licensed under the MIT License. See License.txt in the project root for license information.
44
*--------------------------------------------------------------------------------------------*/
55

6-
import { AzureWizardPromptStep, type IAzureQuickPickItem, type IWizardOptions } from '@microsoft/vscode-azext-utils';
6+
import { AzureWizardPromptStep, type IActionContext, type IAzureQuickPickItem, type IAzureQuickPickOptions, type IWizardOptions } from '@microsoft/vscode-azext-utils';
77
import * as escape from 'escape-string-regexp';
88
import { type FuncVersion } from '../../FuncVersion';
9-
import { JavaBuildTool, ProjectLanguage } from '../../constants';
9+
import { JavaBuildTool, ProjectLanguage, TemplateFilter, templateFilterSetting } from '../../constants';
1010
import { ext } from '../../extensionVariables';
1111
import { localize } from '../../localize';
1212
import { type FunctionTemplateBase, type IFunctionTemplate } from '../../templates/IFunctionTemplate';
1313
import { TemplateSchemaVersion } from '../../templates/TemplateProviderBase';
1414
import { durableUtils } from '../../utils/durableUtils';
1515
import { nonNullProp } from '../../utils/nonNull';
1616
import { isNodeV4Plus, isPythonV2Plus, nodeV4Suffix } from '../../utils/programmingModelUtils';
17-
import { getWorkspaceSetting } from '../../vsCodeConfig/settings';
17+
import { getWorkspaceSetting, updateWorkspaceSetting } from '../../vsCodeConfig/settings';
1818
import { FunctionSubWizard } from './FunctionSubWizard';
1919
import { type IFunctionWizardContext } from './IFunctionWizardContext';
2020
import { JobsListStep } from './JobsListStep';
@@ -39,7 +39,7 @@ export class FunctionListStep extends AzureWizardPromptStep<IFunctionWizardConte
3939
const language: ProjectLanguage = nonNullProp(context, 'language');
4040
const version: FuncVersion = nonNullProp(context, 'version');
4141
const templateProvider = ext.templateProvider.get(context);
42-
const templates: FunctionTemplateBase[] = await templateProvider.getFunctionTemplates(context, context.projectPath, language, context.languageModel, version, context.projectTemplateKey);
42+
const templates: FunctionTemplateBase[] = await templateProvider.getFunctionTemplates(context, context.projectPath, language, context.languageModel, version, TemplateFilter.All, context.projectTemplateKey);
4343
const foundTemplate: FunctionTemplateBase | undefined = templates.find((t: FunctionTemplateBase) => {
4444
if (this._options.templateId) {
4545
const actualId: string = t.id.toLowerCase();
@@ -83,6 +83,10 @@ export class FunctionListStep extends AzureWizardPromptStep<IFunctionWizardConte
8383
}
8484

8585
public async prompt(context: IFunctionWizardContext): Promise<void> {
86+
/* v2 schema doesn't have a template filter setting */
87+
let templateFilter: TemplateFilter = context.templateSchemaVersion === TemplateSchemaVersion.v2 ? TemplateFilter.All :
88+
getWorkspaceSetting<TemplateFilter>(templateFilterSetting, context.projectPath) || TemplateFilter.Verified;
89+
8690
const templateProvider = ext.templateProvider.get(context);
8791
while (!context.functionTemplate) {
8892
let placeHolder: string = this._isProjectWizard ?
@@ -93,13 +97,17 @@ export class FunctionListStep extends AzureWizardPromptStep<IFunctionWizardConte
9397
placeHolder += localize('templateSource', ' (Template source: "{0}")', templateProvider.templateSource)
9498
}
9599

96-
const result: FunctionTemplateBase | TemplatePromptResult =
97-
(await context.ui.showQuickPick(this.getPicks(context),
98-
{ placeHolder, enableGrouping: true })).data;
99-
100+
const result: FunctionTemplateBase | TemplatePromptResult = (await context.ui.showQuickPick(this.getPicks(context, templateFilter), { placeHolder })).data;
100101
if (result === 'skipForNow') {
101102
context.telemetry.properties.templateId = 'skipForNow';
102103
break;
104+
} else if (result === 'changeFilter') {
105+
templateFilter = await promptForTemplateFilter(context);
106+
// can only update setting if it's open in a workspace
107+
if (!this._isProjectWizard || context.openBehavior === 'AlreadyOpen') {
108+
await updateWorkspaceSetting(templateFilterSetting, templateFilter, context.projectPath);
109+
}
110+
context.telemetry.properties.changedFilter = 'true';
103111
} else if (result === 'openAPI') {
104112
context.generateFromOpenAPI = true;
105113
break;
@@ -109,6 +117,8 @@ export class FunctionListStep extends AzureWizardPromptStep<IFunctionWizardConte
109117
} else {
110118
context.functionTemplate = result;
111119
}
120+
121+
context.telemetry.properties.templateFilter = templateFilter;
112122
}
113123
}
114124

@@ -118,18 +128,18 @@ export class FunctionListStep extends AzureWizardPromptStep<IFunctionWizardConte
118128
context.language !== ProjectLanguage.SelfHostedMCPServer;
119129
}
120130

121-
private async getPicks(context: IFunctionWizardContext): Promise<IAzureQuickPickItem<FunctionTemplateBase | TemplatePromptResult>[]> {
131+
private async getPicks(context: IFunctionWizardContext, templateFilter: TemplateFilter): Promise<IAzureQuickPickItem<FunctionTemplateBase | TemplatePromptResult>[]> {
122132
const language: ProjectLanguage = nonNullProp(context, 'language');
123133
const languageModel = context.languageModel;
124134
const version: FuncVersion = nonNullProp(context, 'version');
125135
const templateProvider = ext.templateProvider.get(context);
126136

127-
const templates: FunctionTemplateBase[] = await templateProvider.getFunctionTemplates(context, context.projectPath, language, context.languageModel, version, context.projectTemplateKey);
137+
const templates: FunctionTemplateBase[] = await templateProvider.getFunctionTemplates(context, context.projectPath, language, context.languageModel, version, templateFilter, context.projectTemplateKey);
128138
context.telemetry.measurements.templateCount = templates.length;
129139
const picks: IAzureQuickPickItem<FunctionTemplateBase | TemplatePromptResult>[] = templates
130140
.filter((t) => !(doesTemplateRequireExistingStorageSetup(t.id, language) && !context.hasDurableStorage))
131-
.sort((a, b) => sortTemplates(a, b))
132-
.map(t => { return { label: t.name, data: t, group: t.templateFilter }; });
141+
.sort((a, b) => sortTemplates(a, b, templateFilter))
142+
.map(t => { return { label: t.name, data: t }; });
133143

134144
if (this._isProjectWizard) {
135145
picks.unshift({
@@ -157,6 +167,15 @@ export class FunctionListStep extends AzureWizardPromptStep<IFunctionWizardConte
157167
suppressPersistence: true
158168
});
159169
}
170+
if (context.templateSchemaVersion !== TemplateSchemaVersion.v2) {
171+
// don't offer template filter for v2 schema
172+
picks.push({
173+
label: localize('selectFilter', '$(gear) Change template filter'),
174+
description: localize('currentFilter', 'Current: {0}', templateFilter),
175+
data: 'changeFilter',
176+
suppressPersistence: true
177+
});
178+
}
160179

161180
if (getWorkspaceSetting<boolean>('showReloadTemplates')) {
162181
picks.push({
@@ -176,7 +195,18 @@ interface IFunctionListStepOptions {
176195
functionSettings: { [key: string]: string | undefined } | undefined;
177196
}
178197

179-
type TemplatePromptResult = 'skipForNow' | 'openAPI' | 'reloadTemplates';
198+
type TemplatePromptResult = 'changeFilter' | 'skipForNow' | 'openAPI' | 'reloadTemplates';
199+
200+
async function promptForTemplateFilter(context: IActionContext): Promise<TemplateFilter> {
201+
const picks: IAzureQuickPickItem<TemplateFilter>[] = [
202+
{ label: TemplateFilter.Verified, description: localize('verifiedDescription', '(Subset of "Core" that has been verified in VS Code)'), data: TemplateFilter.Verified },
203+
{ label: TemplateFilter.Core, data: TemplateFilter.Core },
204+
{ label: TemplateFilter.All, data: TemplateFilter.All }
205+
];
206+
207+
const options: IAzureQuickPickOptions = { suppressPersistence: true, placeHolder: localize('selectFilter', 'Select a template filter') };
208+
return (await context.ui.showQuickPick(picks, options)).data;
209+
}
180210

181211
// Todo: https://github.com/microsoft/vscode-azurefunctions/issues/3529
182212
// Identify and filter out Durable Function templates requiring a pre-existing storage setup
@@ -198,17 +228,21 @@ function doesTemplateRequireExistingStorageSetup(templateId: string, language?:
198228
* If templateFilter is verified, puts HttpTrigger/TimerTrigger at the top since they're the most popular
199229
* Otherwise sort alphabetically
200230
*/
201-
function sortTemplates(a: FunctionTemplateBase, b: FunctionTemplateBase): number {
202-
function getPriority(id: string): number {
203-
if (/\bhttptrigger\b/i.test(id)) { // Plain http trigger
204-
return 1;
205-
} else if (/\bhttptrigger/i.test(id)) { // Http trigger with any extra pizazz
206-
return 2;
207-
} else if (/\btimertrigger\b/i.test(id)) {
208-
return 3;
209-
} else {
210-
return a.name.localeCompare(b.name) === -1 ? 4 : 5;
231+
function sortTemplates(a: FunctionTemplateBase, b: FunctionTemplateBase, templateFilter: TemplateFilter): number {
232+
if (templateFilter === TemplateFilter.Verified) {
233+
function getPriority(id: string): number {
234+
if (/\bhttptrigger\b/i.test(id)) { // Plain http trigger
235+
return 1;
236+
} else if (/\bhttptrigger/i.test(id)) { // Http trigger with any extra pizazz
237+
return 2;
238+
} else if (/\btimertrigger\b/i.test(id)) {
239+
return 3;
240+
} else {
241+
return 4;
242+
}
211243
}
244+
return getPriority(a.id) - getPriority(b.id);
212245
}
213-
return getPriority(a.id) - getPriority(b.id);
246+
247+
return a.name.localeCompare(b.name);
214248
}

src/constants.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ export const projectLanguageSetting: string = 'projectLanguage';
1111
export const projectLanguageModelSetting: string = 'projectLanguageModel';
1212
export const funcVersionSetting: string = 'projectRuntime'; // Using this name for the sake of backwards compatability even though it's not the most accurate
1313
export const projectSubpathSetting: string = 'projectSubpath';
14+
export const templateFilterSetting: string = 'templateFilter';
1415
export const deploySubpathSetting: string = 'deploySubpath';
1516
export const templateVersionSetting: string = 'templateVersion';
1617
export const preDeployTaskSetting: string = 'preDeployTask';

src/templates/CentralTemplateProvider.ts

Lines changed: 11 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -83,20 +83,18 @@ export class CentralTemplateProvider implements Disposable {
8383
}
8484

8585
/* Ignored by the v2 schema */
86-
public async getFunctionTemplates(context: IActionContext, projectPath: string | undefined, language: ProjectLanguage, languageModel: number | undefined, version: FuncVersion, projectTemplateKey: string | undefined): Promise<FunctionTemplateBase[]> {
86+
public async getFunctionTemplates(context: IActionContext, projectPath: string | undefined, language: ProjectLanguage, languageModel: number | undefined, version: FuncVersion, templateFilter: TemplateFilter, projectTemplateKey: string | undefined): Promise<FunctionTemplateBase[]> {
8787
const templates: ITemplates = await this.getTemplates(context, projectPath, language, languageModel, version, projectTemplateKey);
88-
for (const template of templates.functionTemplates) {
89-
// by default, all templates will be categorized as 'All'
90-
template.templateFilter = TemplateFilter.All;
91-
const verifiedTemplateIds = getScriptVerifiedTemplateIds(version).concat(getDotnetVerifiedTemplateIds(version)).concat(getJavaVerifiedTemplateIds().concat(getBallerinaVerifiedTemplateIds()));
92-
if (verifiedTemplateIds.find(vt => typeof vt === 'string' ? vt === template.id : vt.test(template.id))) {
93-
template.templateFilter = TemplateFilter.Verified;
94-
} else if ((template as IFunctionTemplate).categories.find((c: TemplateCategory) => c === TemplateCategory.Core) !== undefined) {
95-
template.templateFilter = TemplateFilter.Core;
96-
}
88+
switch (templateFilter) {
89+
case TemplateFilter.All:
90+
return templates.functionTemplates;
91+
case TemplateFilter.Core:
92+
return templates.functionTemplates.filter((t: IFunctionTemplate) => t.categories.find((c: TemplateCategory) => c === TemplateCategory.Core) !== undefined);
93+
case TemplateFilter.Verified:
94+
default:
95+
const verifiedTemplateIds = getScriptVerifiedTemplateIds(version).concat(getDotnetVerifiedTemplateIds(version)).concat(getJavaVerifiedTemplateIds().concat(getBallerinaVerifiedTemplateIds()));
96+
return templates.functionTemplates.filter((t: IFunctionTemplate) => verifiedTemplateIds.find(vt => typeof vt === 'string' ? vt === t.id : vt.test(t.id)));
9797
}
98-
99-
return templates.functionTemplates;
10098
}
10199

102100
public async clearTemplateCache(context: IActionContext, projectPath: string | undefined, language: ProjectLanguage, languageModel: number | undefined, version: FuncVersion): Promise<void> {
@@ -119,7 +117,7 @@ export class CentralTemplateProvider implements Disposable {
119117

120118
public async tryGetSampleData(context: IActionContext, version: FuncVersion, triggerBindingType: string): Promise<string | undefined> {
121119
try {
122-
const templates: IScriptFunctionTemplate[] = <IScriptFunctionTemplate[]>await this.getFunctionTemplates(context, undefined, ProjectLanguage.JavaScript, undefined, version, undefined);
120+
const templates: IScriptFunctionTemplate[] = <IScriptFunctionTemplate[]>await this.getFunctionTemplates(context, undefined, ProjectLanguage.JavaScript, undefined, version, TemplateFilter.All, undefined);
123121
const template: IScriptFunctionTemplate | undefined = templates.find(t => t.functionJson.triggerBinding?.type?.toLowerCase() === triggerBindingType.toLowerCase());
124122
return template?.templateFiles['sample.dat'];
125123
} catch {

src/templates/IFunctionTemplate.ts

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
* Licensed under the MIT License. See License.txt in the project root for license information.
44
*--------------------------------------------------------------------------------------------*/
55

6-
import { type ProjectLanguage, type TemplateFilter } from '../constants';
6+
import { type ProjectLanguage } from '../constants';
77
import { type IBindingSetting } from './IBindingTemplate';
88
import { type TemplateSchemaVersion } from './TemplateProviderBase';
99
import { type ParsedJob, type RawTemplateV2 } from './script/parseScriptTemplatesV2';
@@ -46,5 +46,4 @@ export interface FunctionTemplateBase {
4646
isTimerTrigger: boolean;
4747
isMcpTrigger: boolean;
4848
templateSchemaVersion: TemplateSchemaVersion
49-
templateFilter?: TemplateFilter; // defaults to All
5049
}

src/templates/dotnet/parseDotnetTemplates.ts

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -86,8 +86,7 @@ function parseDotnetTemplate(rawTemplate: IRawTemplate): IFunctionTemplate {
8686
userPromptedSettings: userPromptedSettings,
8787
categories: [TemplateCategory.Core], // Dotnet templates do not have category information, so display all templates as if they are in the 'core' category
8888
isDynamicConcurrent: (rawTemplate.Identity.includes('ServiceBusQueueTrigger') || rawTemplate.Identity.includes('BlobTrigger') || rawTemplate.Identity.includes('QueueTrigger')) ? true : false,
89-
templateSchemaVersion: TemplateSchemaVersion.v1,
90-
templateFilter: TemplateFilter.All
89+
templateSchemaVersion: TemplateSchemaVersion.v1
9190
};
9291
}
9392

@@ -124,7 +123,7 @@ async function copyCSharpSettingsFromJS(csharpTemplates: IFunctionTemplate[], ve
124123
jsContext.telemetry.properties.isActivationEvent = 'true';
125124

126125
const templateProvider = ext.templateProvider.get(jsContext);
127-
const jsTemplates: FunctionTemplateBase[] = await templateProvider.getFunctionTemplates(jsContext, undefined, ProjectLanguage.JavaScript, undefined, version, undefined);
126+
const jsTemplates: FunctionTemplateBase[] = await templateProvider.getFunctionTemplates(jsContext, undefined, ProjectLanguage.JavaScript, undefined, version, TemplateFilter.All, undefined);
128127
for (const csharpTemplate of csharpTemplates) {
129128
assertTemplateIsV1(csharpTemplate);
130129
csharpTemplate.templateSchemaVersion = TemplateSchemaVersion.v1;

src/vsCodeConfig/verifyVSCodeConfigOnActivate.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ import * as path from 'path';
88
import type * as vscode from 'vscode';
99
import { tryGetFunctionProjectRoot } from '../commands/createNewProject/verifyIsProject';
1010
import { initProjectForVSCode } from '../commands/initProjectForVSCode/initProjectForVSCode';
11-
import { funcVersionSetting, ProjectLanguage, projectLanguageModelSetting, projectLanguageSetting } from '../constants';
11+
import { funcVersionSetting, ProjectLanguage, projectLanguageModelSetting, projectLanguageSetting, TemplateFilter } from '../constants';
1212
import { ext } from '../extensionVariables';
1313
import { tryParseFuncVersion, type FuncVersion } from '../FuncVersion';
1414
import { localize } from '../localize';
@@ -38,7 +38,7 @@ export async function verifyVSCodeConfigOnActivate(context: IActionContext, fold
3838
templatesContext.telemetry.properties.isActivationEvent = 'true';
3939
templatesContext.errorHandling.suppressDisplay = true;
4040
const templateProvider = ext.templateProvider.get(templatesContext);
41-
await templateProvider.getFunctionTemplates(templatesContext, projectPath, language, languageModel, version, undefined);
41+
await templateProvider.getFunctionTemplates(templatesContext, projectPath, language, languageModel, version, TemplateFilter.Verified, undefined);
4242
});
4343

4444
let isDotnet: boolean = false;

0 commit comments

Comments
 (0)