33 * Licensed under the MIT License. See License.txt in the project root for license information.
44 *--------------------------------------------------------------------------------------------*/
55
6- import { AzExtFsExtra , AzureWizardExecuteStepWithActivityOutput } from '@microsoft/vscode-azext-utils' ;
6+ import { AzExtFsExtra , AzureWizardExecuteStepWithActivityOutput , parseError , type IParsedError } from '@microsoft/vscode-azext-utils' ;
77import * as path from "path" ;
88import { type Progress } from 'vscode' ;
99import { ConnectionKey , DurableBackend , hostFileName , ProjectLanguage } from '../../../constants' ;
1010import { viewOutput } from '../../../constants-nls' ;
1111import { ext } from '../../../extensionVariables' ;
12- import { type IHostJsonV2 } from '../../../funcConfig/host' ;
12+ import { type IDTSTaskJson , type IHostJsonV2 , type INetheriteTaskJson , type ISqlTaskJson , type IStorageTaskJson } from '../../../funcConfig/host' ;
1313import { MismatchBehavior , setLocalAppSetting } from '../../../funcConfig/local.settings' ;
1414import { localize } from '../../../localize' ;
15+ import { cpUtils } from '../../../utils/cpUtils' ;
1516import { durableUtils } from '../../../utils/durableUtils' ;
17+ import { pythonUtils } from '../../../utils/pythonUtils' ;
18+ import { venvUtils } from '../../../utils/venvUtils' ;
1619import { type IFunctionWizardContext } from '../IFunctionWizardContext' ;
1720
1821export class DurableProjectConfigureStep < T extends IFunctionWizardContext > extends AzureWizardExecuteStepWithActivityOutput < T > {
@@ -25,22 +28,22 @@ export class DurableProjectConfigureStep<T extends IFunctionWizardContext> exten
2528 protected getOutputLogFail ( _context : T ) : string {
2629 return localize ( 'failedToConfigureDurableProject' , 'Failed to configure durable project settings.' ) ;
2730 }
28- protected getOutputLogProgress ( _context : T ) : string {
29- return localize ( 'configuringDurableProject' , 'Configuring durable project settings...' ) ;
30- }
3131 protected preDeployTask : string = 'funcHostStart' ;
3232 public stepName : string = 'DurableProjectConfigureStep' ;
3333 public priority : number = 225 ;
3434
35- public async execute ( context : T , _progress : Progress < { message ?: string ; increment ?: number } > ) : Promise < void > {
35+ public async execute ( context : T , progress : Progress < { message ?: string ; increment ?: number } > ) : Promise < void > {
36+ progress . report ( { message : localize ( 'configuringDurableProject' , 'Configuring durable project settings...' ) } ) ;
3637 await this . configureHostAndLocalSettingsJson ( context ) ;
37- await durableUtils . tryInstallDurableDependencies ( context ) ;
38+ await this . tryInstallDurableDependencies ( context ) ;
3839 }
3940
4041 public shouldExecute ( context : T ) : boolean {
4142 return ! ! context . newDurableStorageType ;
4243 }
4344
45+ // #region Durable Task Local Settings
46+
4447 private async configureHostAndLocalSettingsJson ( context : T ) : Promise < void > {
4548 const hostJsonPath : string = path . join ( context . projectPath , hostFileName ) ;
4649
@@ -65,15 +68,15 @@ export class DurableProjectConfigureStep<T extends IFunctionWizardContext> exten
6568
6669 switch ( context . newDurableStorageType ) {
6770 case DurableBackend . Storage :
68- hostJson . extensions . durableTask = durableUtils . getDefaultStorageTaskConfig ( ) ;
71+ hostJson . extensions . durableTask = this . getDefaultStorageTaskConfig ( ) ;
6972 // Omit setting azureWebJobsStorage since it should already be initialized during 'createNewProject'
7073 break ;
7174 case DurableBackend . Netherite :
72- hostJson . extensions . durableTask = durableUtils . getDefaultNetheriteTaskConfig ( ) ;
75+ hostJson . extensions . durableTask = this . getDefaultNetheriteTaskConfig ( ) ;
7376 await setLocalAppSetting ( context , context . projectPath , ConnectionKey . EventHubs , '' , MismatchBehavior . Overwrite ) ;
7477 break ;
7578 case DurableBackend . DTS :
76- hostJson . extensions . durableTask = durableUtils . getDefaultDTSTaskConfig ( ) ;
79+ hostJson . extensions . durableTask = this . getDefaultDTSTaskConfig ( ) ;
7780 // Non- .NET projects require a special preview extension bundle to work properly
7881 // Todo: Remove once this functionality is out of preview
7982 if ( context . language !== ProjectLanguage . CSharp && context . language !== ProjectLanguage . FSharp ) {
@@ -84,15 +87,147 @@ export class DurableProjectConfigureStep<T extends IFunctionWizardContext> exten
8487 ext . outputChannel . appendLog ( localize ( 'extensionBundlePreview' , 'Updated "host.json" extension bundle to preview version to enable new DTS features.' ) ) ;
8588 }
8689 await setLocalAppSetting ( context , context . projectPath , ConnectionKey . DTS , '' , MismatchBehavior . Overwrite ) ;
87- await setLocalAppSetting ( context , context . projectPath , ConnectionKey . DTSHub , 'default ' , MismatchBehavior . Overwrite ) ;
90+ await setLocalAppSetting ( context , context . projectPath , ConnectionKey . DTSHub , '' , MismatchBehavior . Overwrite ) ;
8891 break ;
8992 case DurableBackend . SQL :
90- hostJson . extensions . durableTask = durableUtils . getDefaultSqlTaskConfig ( ) ;
93+ hostJson . extensions . durableTask = this . getDefaultSqlTaskConfig ( ) ;
9194 await setLocalAppSetting ( context , context . projectPath , ConnectionKey . SQL , '' , MismatchBehavior . Overwrite ) ;
9295 break ;
9396 default :
9497 }
9598
9699 await AzExtFsExtra . writeJSON ( hostJsonPath , hostJson ) ;
97100 }
101+
102+ private getDefaultStorageTaskConfig ( ) : IStorageTaskJson {
103+ return {
104+ storageProvider : {
105+ type : DurableBackend . Storage ,
106+ }
107+ } ;
108+ }
109+
110+ private getDefaultNetheriteTaskConfig ( ) : INetheriteTaskJson {
111+ return {
112+ hubName : '' ,
113+ useGracefulShutdown : true ,
114+ storageProvider : {
115+ type : DurableBackend . Netherite ,
116+ StorageConnectionName : ConnectionKey . Storage ,
117+ EventHubsConnectionName : ConnectionKey . EventHubs ,
118+ }
119+ } ;
120+ }
121+
122+ private getDefaultDTSTaskConfig ( ) : IDTSTaskJson {
123+ return {
124+ hubName : '%TASKHUB_NAME%' ,
125+ storageProvider : {
126+ type : DurableBackend . DTS ,
127+ connectionStringName : ConnectionKey . DTS ,
128+ }
129+ } ;
130+ }
131+
132+ private getDefaultSqlTaskConfig ( ) : ISqlTaskJson {
133+ return {
134+ storageProvider : {
135+ type : DurableBackend . SQL ,
136+ connectionStringName : ConnectionKey . SQL ,
137+ taskEventLockTimeout : "00:02:00" ,
138+ createDatabaseIfNotExists : true ,
139+ }
140+ } ;
141+ }
142+
143+ // #endregion Durable Task Local Settings
144+
145+ // #region Install Durable Dependencies
146+
147+ private async tryInstallDurableDependencies ( context : IFunctionWizardContext ) : Promise < void > {
148+ switch ( context . language ) {
149+ case ProjectLanguage . Java :
150+ // Todo: Revisit when adding Java implementation
151+ break ;
152+ case ProjectLanguage . CSharp :
153+ case ProjectLanguage . FSharp :
154+ await this . installDotnetDependencies ( context ) ;
155+ break ;
156+ case ProjectLanguage . JavaScript :
157+ case ProjectLanguage . TypeScript :
158+ await this . installNodeDependencies ( context ) ;
159+ break ;
160+ case ProjectLanguage . Python :
161+ await pythonUtils . addDependencyToRequirements ( durableUtils . pythonDfPackage , context . projectPath ) ;
162+ await venvUtils . runPipInstallCommandIfPossible ( context . projectPath ) ;
163+ break ;
164+ case ProjectLanguage . PowerShell :
165+ // Todo: Revisit when adding PowerShell implementation
166+ break ;
167+ default :
168+ }
169+ }
170+
171+ private async installDotnetDependencies ( context : IFunctionWizardContext ) : Promise < void > {
172+ const packages : { name : string ; prerelease ?: boolean } [ ] = [ ] ;
173+ const isDotnetIsolated : boolean = / I s o l a t e d / i. test ( context . functionTemplate ?. id ?? '' ) ;
174+
175+ switch ( context . newDurableStorageType ) {
176+ case DurableBackend . Netherite :
177+ isDotnetIsolated ?
178+ packages . push ( { name : durableUtils . dotnetIsolatedDfNetheritePackage } ) :
179+ packages . push ( { name : durableUtils . dotnetInProcDfNetheritePackage } ) ;
180+ break ;
181+ case DurableBackend . DTS :
182+ // Todo: Remove prerelease flag once this functionality is out of preview
183+ isDotnetIsolated ?
184+ packages . push ( { name : durableUtils . dotnetIsolatedDTSPackage , prerelease : true } ) :
185+ packages . push ( { name : durableUtils . dotnetInProcDTSPackage , prerelease : true } ) ;
186+ break ;
187+ case DurableBackend . SQL :
188+ isDotnetIsolated ?
189+ packages . push ( { name : durableUtils . dotnetIsolatedDfSqlPackage } ) :
190+ packages . push ( { name : durableUtils . dotnetInProcDfSqlPackage } ) ;
191+ break ;
192+ case DurableBackend . Storage :
193+ default :
194+ }
195+
196+ // Although the templates should incorporate this package already, it is often included with an out-dated version
197+ // which can lead to errors on first run. To improve this experience for our users, ensure that the latest version is used.
198+ if ( ! isDotnetIsolated ) {
199+ packages . push ( { name : durableUtils . dotnetInProcDfBasePackage } ) ;
200+ }
201+
202+ const failedPackages : string [ ] = [ ] ;
203+ for ( const p of packages ) {
204+ try {
205+ const packageArgs : string [ ] = [ p . name ] ;
206+ if ( p . prerelease ) {
207+ packageArgs . push ( '--prerelease' ) ;
208+ }
209+ await cpUtils . executeCommand ( ext . outputChannel , context . projectPath , 'dotnet' , 'add' , 'package' , ...packageArgs ) ;
210+ } catch {
211+ failedPackages . push ( p . name ) ;
212+ }
213+ }
214+
215+ if ( failedPackages . length ) {
216+ ext . outputChannel . appendLog ( localize ( 'durableDependencyInstallFailed' , 'WARNING: Failed to install and update Durable Functions NuGet packages to the root .csproj project file. You may need to install the following packages manually: "{0}".' , failedPackages . join ( '", "' ) ) ) ;
217+ }
218+ }
219+
220+ private async installNodeDependencies ( context : IFunctionWizardContext ) : Promise < void > {
221+ try {
222+ const packageVersion = context . languageModel === 4 ? '3' : '2' ;
223+ await cpUtils . executeCommand ( ext . outputChannel , context . projectPath , 'npm' , 'install' , `${ durableUtils . nodeDfPackage } @${ packageVersion } ` ) ;
224+ } catch ( error ) {
225+ const pError : IParsedError = parseError ( error ) ;
226+ const dfDepInstallFailed : string = localize ( 'failedToAddDurableNodeDependency' , 'Failed to add or install the "{0}" dependency. Please inspect and verify if it needs to be added manually.' , durableUtils . nodeDfPackage ) ;
227+ ext . outputChannel . appendLog ( pError . message ) ;
228+ ext . outputChannel . appendLog ( dfDepInstallFailed ) ;
229+ }
230+ }
231+
232+ // #endregion Install Durable Dependencies
98233}
0 commit comments