Skip to content

Commit 5c6400c

Browse files
authored
Add DTS preview support for Create Function and remove Netherite from list of new storage options (#4527)
1 parent 60ba299 commit 5c6400c

File tree

6 files changed

+74
-33
lines changed

6 files changed

+74
-33
lines changed

src/commands/createFunction/IFunctionWizardContext.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
*--------------------------------------------------------------------------------------------*/
55

66
import { type ExecuteActivityContext, type ISubscriptionContext } from "@microsoft/vscode-azext-utils";
7-
import { type DurableBackendValues } from "../../constants";
7+
import { type DurableBackend } from "../../constants";
88
import { type BindingSettingValue } from "../../funcConfig/function";
99
import { type IBindingSetting } from "../../templates/IBindingTemplate";
1010
import { type FunctionTemplateBase } from "../../templates/IFunctionTemplate";
@@ -17,7 +17,7 @@ export interface IFunctionWizardContext extends Partial<ISubscriptionContext>, I
1717

1818
// Durable Functions
1919
hasDurableStorage?: boolean;
20-
newDurableStorageType?: DurableBackendValues;
20+
newDurableStorageType?: DurableBackend;
2121

2222
useStorageEmulator?: boolean;
2323
}

src/commands/createFunction/durableSteps/DurableProjectConfigureStep.ts

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
import { AzExtFsExtra, AzureWizardExecuteStepWithActivityOutput } from '@microsoft/vscode-azext-utils';
77
import * as path from "path";
88
import { type Progress } from 'vscode';
9-
import { ConnectionKey, DurableBackend, hostFileName } from '../../../constants';
9+
import { ConnectionKey, DurableBackend, hostFileName, ProjectLanguage } from '../../../constants';
1010
import { viewOutput } from '../../../constants-nls';
1111
import { ext } from '../../../extensionVariables';
1212
import { type IHostJsonV2 } from '../../../funcConfig/host';
@@ -72,6 +72,20 @@ export class DurableProjectConfigureStep<T extends IFunctionWizardContext> exten
7272
hostJson.extensions.durableTask = durableUtils.getDefaultNetheriteTaskConfig();
7373
await setLocalAppSetting(context, context.projectPath, ConnectionKey.EventHubs, '', MismatchBehavior.Overwrite);
7474
break;
75+
case DurableBackend.DTS:
76+
hostJson.extensions.durableTask = durableUtils.getDefaultDTSTaskConfig();
77+
// Non- .NET projects require a special preview extension bundle to work properly
78+
// Todo: Remove once this functionality is out of preview
79+
if (context.language !== ProjectLanguage.CSharp && context.language !== ProjectLanguage.FSharp) {
80+
hostJson.extensionBundle = {
81+
id: 'Microsoft.Azure.Functions.ExtensionBundle.Preview',
82+
version: '[4.29.0, 5.0.0)',
83+
};
84+
ext.outputChannel.appendLog(localize('extensionBundlePreview', 'Updated "host.json" extension bundle to preview version to enable new DTS features.'));
85+
}
86+
await setLocalAppSetting(context, context.projectPath, ConnectionKey.DTS, '', MismatchBehavior.Overwrite);
87+
await setLocalAppSetting(context, context.projectPath, ConnectionKey.DTSHub, 'default', MismatchBehavior.Overwrite);
88+
break;
7589
case DurableBackend.SQL:
7690
hostJson.extensions.durableTask = durableUtils.getDefaultSqlTaskConfig();
7791
await setLocalAppSetting(context, context.projectPath, ConnectionKey.SQL, '', MismatchBehavior.Overwrite);

src/commands/createFunction/durableSteps/DurableStorageTypePromptStep.ts

Lines changed: 6 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55

66
import { AzureWizardPromptStep, openUrl, type IAzureQuickPickItem, type IWizardOptions } from "@microsoft/vscode-azext-utils";
77
import { DurableBackend, type DurableBackendValues } from "../../../constants";
8-
import { defaultDescription } from "../../../constants-nls";
8+
import { defaultDescription, previewDescription } from "../../../constants-nls";
99
import { localize } from "../../../localize";
1010
import { FunctionSubWizard } from "../FunctionSubWizard";
1111
import { type IFunctionWizardContext } from "../IFunctionWizardContext";
@@ -19,24 +19,19 @@ export class DurableStorageTypePromptStep<T extends IFunctionWizardContext> exte
1919
}
2020

2121
public async prompt(context: T): Promise<void> {
22-
const durableStorageLabels: string[] = [
23-
'Azure Storage',
24-
'Netherite',
25-
'MSSQL'
26-
];
2722
const durableStorageInfo: string = localize('durableStorageInfo', '$(link-external) Learn more about the tradeoffs between storage providers');
2823

2924
const placeHolder: string = localize('chooseDurableStorageType', 'Choose a durable storage type.');
3025
const picks: IAzureQuickPickItem<DurableBackendValues | undefined>[] = [
31-
{ label: durableStorageLabels[0], description: defaultDescription, data: DurableBackend.Storage, suppressPersistence: true },
32-
{ label: durableStorageLabels[1], data: DurableBackend.Netherite, suppressPersistence: true },
33-
{ label: durableStorageLabels[2], data: DurableBackend.SQL, suppressPersistence: true },
34-
{ label: durableStorageInfo, data: undefined, suppressPersistence: true }
26+
{ label: 'Azure Storage', description: defaultDescription, data: DurableBackend.Storage },
27+
{ label: 'Durable Task Scheduler', description: previewDescription, data: DurableBackend.DTS },
28+
{ label: 'MSSQL', data: DurableBackend.SQL },
29+
{ label: durableStorageInfo, data: undefined }
3530
];
3631

3732
let pick: DurableBackendValues | undefined;
3833
while (!pick) {
39-
pick = (await context.ui.showQuickPick(picks, { placeHolder })).data;
34+
pick = (await context.ui.showQuickPick(picks, { placeHolder, suppressPersistence: true })).data;
4035
if (!pick) {
4136
await openUrl('https://aka.ms/durable-storage-providers');
4237
}

src/constants.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -99,6 +99,8 @@ export enum ConnectionKey {
9999
Storage = 'AzureWebJobsStorage',
100100
StorageIdentity = 'AzureWebJobsStorage__accountName',
101101
EventHubs = 'EventHubsConnection',
102+
DTS = 'DURABLE_TASK_SCHEDULER_CONNECTION_STRING',
103+
DTSHub = 'TASKHUB_NAME',
102104
SQL = 'SQLDB_Connection'
103105
}
104106

@@ -120,7 +122,8 @@ export enum ConnectionType {
120122
export enum DurableBackend {
121123
Storage = 'AzureStorage',
122124
Netherite = 'Netherite',
123-
SQL = "mssql"
125+
DTS = 'azureManaged',
126+
SQL = 'mssql',
124127
}
125128

126129
export type ConnectionTypeValues = typeof ConnectionType[keyof typeof ConnectionType];

src/funcConfig/host.ts

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ export interface IHostJsonV2 {
3434
};
3535
}
3636

37-
export type IDurableTaskJson = IStorageTaskJson | INetheriteTaskJson | ISqlTaskJson;
37+
export type IDurableTaskJson = IStorageTaskJson | INetheriteTaskJson | IDTSTaskJson | ISqlTaskJson;
3838

3939
export interface IStorageTaskJson {
4040
storageProvider?: {
@@ -53,6 +53,14 @@ export interface INetheriteTaskJson {
5353
}
5454
}
5555

56+
export interface IDTSTaskJson {
57+
hubName?: string;
58+
storageProvider?: {
59+
type?: DurableBackend.DTS;
60+
connectionStringName?: string;
61+
}
62+
}
63+
5664
export interface ISqlTaskJson {
5765
storageProvider?: {
5866
type?: DurableBackend.SQL;

src/utils/durableUtils.ts

Lines changed: 38 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ import * as xml2js from "xml2js";
1010
import { type IFunctionWizardContext } from "../commands/createFunction/IFunctionWizardContext";
1111
import { ConnectionKey, DurableBackend, ProjectLanguage, hostFileName, requirementsFileName, type DurableBackendValues } from "../constants";
1212
import { ext } from "../extensionVariables";
13-
import { type IHostJsonV2, type INetheriteTaskJson, type ISqlTaskJson, type IStorageTaskJson } from "../funcConfig/host";
13+
import { type IDTSTaskJson, type IHostJsonV2, type INetheriteTaskJson, type ISqlTaskJson, type IStorageTaskJson } from "../funcConfig/host";
1414
import { localize } from "../localize";
1515
import { cpUtils } from "./cpUtils";
1616
import { dotnetUtils } from "./dotnetUtils";
@@ -24,6 +24,8 @@ export namespace durableUtils {
2424
export const dotnetIsolatedDfSqlPackage: string = 'Microsoft.Azure.Functions.Worker.Extensions.DurableTask.SqlServer';
2525
export const dotnetInProcDfNetheritePackage: string = 'Microsoft.Azure.DurableTask.Netherite.AzureFunctions';
2626
export const dotnetIsolatedDfNetheritePackage: string = 'Microsoft.Azure.Functions.Worker.Extensions.DurableTask.Netherite';
27+
export const dotnetInProcDTSPackage: string = 'Microsoft.Azure.WebJobs.Extensions.DurableTask.AzureManaged';
28+
export const dotnetIsolatedDTSPackage: string = 'Microsoft.Azure.Functions.Worker.Extensions.DurableTask.AzureManaged';
2729
export const dotnetInProcDfBasePackage: string = 'Microsoft.Azure.WebJobs.Extensions.DurableTask';
2830
export const nodeDfPackage: string = 'durable-functions';
2931
export const pythonDfPackage: string = 'azure-functions-durable';
@@ -62,6 +64,8 @@ export namespace durableUtils {
6264
switch (hostStorageType) {
6365
case DurableBackend.Netherite:
6466
return DurableBackend.Netherite;
67+
case DurableBackend.DTS:
68+
return DurableBackend.DTS;
6569
case DurableBackend.SQL:
6670
return DurableBackend.SQL;
6771
case DurableBackend.Storage:
@@ -150,39 +154,46 @@ export namespace durableUtils {
150154
}
151155

152156
async function installDotnetDependencies(context: IFunctionWizardContext): Promise<void> {
153-
const packageNames: string[] = [];
157+
const packages: { name: string; prerelease?: boolean }[] = [];
154158
const isDotnetIsolated: boolean = /Isolated/i.test(context.functionTemplate?.id ?? '');
155159

156160
switch (context.newDurableStorageType) {
157161
case DurableBackend.Netherite:
158162
isDotnetIsolated ?
159-
packageNames.push(dotnetIsolatedDfNetheritePackage) :
160-
packageNames.push(dotnetInProcDfNetheritePackage);
163+
packages.push({ name: dotnetIsolatedDfNetheritePackage }) :
164+
packages.push({ name: dotnetInProcDfNetheritePackage });
165+
break;
166+
case DurableBackend.DTS:
167+
// Todo: Remove prerelease flag once this functionality is out of preview
168+
isDotnetIsolated ?
169+
packages.push({ name: dotnetIsolatedDTSPackage, prerelease: true }) :
170+
packages.push({ name: dotnetInProcDTSPackage, prerelease: true });
161171
break;
162172
case DurableBackend.SQL:
163173
isDotnetIsolated ?
164-
packageNames.push(dotnetIsolatedDfSqlPackage) :
165-
packageNames.push(dotnetInProcDfSqlPackage);
174+
packages.push({ name: dotnetIsolatedDfSqlPackage }) :
175+
packages.push({ name: dotnetInProcDfSqlPackage });
166176
break;
167177
case DurableBackend.Storage:
168178
default:
169179
}
170180

171-
/*
172-
* https://github.com/microsoft/vscode-azurefunctions/issues/3599
173-
* Seems that the package arrives out-dated and needs to be updated to at least 2.9.1;
174-
* otherwise, error appears when running with sql backend
175-
*/
181+
// Although the templates should incorporate this package already, it is often included with an out-dated version
182+
// which can lead to errors on first run. To improve this experience for our users, ensure that the latest version is used.
176183
if (!isDotnetIsolated) {
177-
packageNames.push(dotnetInProcDfBasePackage);
184+
packages.push({ name: dotnetInProcDfBasePackage });
178185
}
179186

180187
const failedPackages: string[] = [];
181-
for (const packageName of packageNames) {
188+
for (const p of packages) {
182189
try {
183-
await cpUtils.executeCommand(ext.outputChannel, context.projectPath, 'dotnet', 'add', 'package', packageName);
190+
const packageArgs: string[] = [p.name];
191+
if (p.prerelease) {
192+
packageArgs.push('--prerelease');
193+
}
194+
await cpUtils.executeCommand(ext.outputChannel, context.projectPath, 'dotnet', 'add', 'package', ...packageArgs);
184195
} catch {
185-
failedPackages.push(packageName);
196+
failedPackages.push(p.name);
186197
}
187198
}
188199

@@ -215,9 +226,9 @@ export namespace durableUtils {
215226
};
216227
}
217228

218-
export function getDefaultNetheriteTaskConfig(hubName?: string): INetheriteTaskJson {
229+
export function getDefaultNetheriteTaskConfig(hubName: string = ''): INetheriteTaskJson {
219230
return {
220-
hubName: hubName || '',
231+
hubName,
221232
useGracefulShutdown: true,
222233
storageProvider: {
223234
type: DurableBackend.Netherite,
@@ -227,6 +238,16 @@ export namespace durableUtils {
227238
};
228239
}
229240

241+
export function getDefaultDTSTaskConfig(): IDTSTaskJson {
242+
return {
243+
hubName: '%TASKHUB_NAME%',
244+
storageProvider: {
245+
type: DurableBackend.DTS,
246+
connectionStringName: ConnectionKey.DTS,
247+
}
248+
};
249+
}
250+
230251
export function getDefaultSqlTaskConfig(): ISqlTaskJson {
231252
return {
232253
storageProvider: {

0 commit comments

Comments
 (0)