3
3
* Licensed under the MIT License. See License.txt in the project root for license information.
4
4
*--------------------------------------------------------------------------------------------*/
5
5
6
- import { AzExtFsExtra , AzureWizardExecuteStepWithActivityOutput } from '@microsoft/vscode-azext-utils' ;
6
+ import { AzExtFsExtra , AzureWizardExecuteStepWithActivityOutput , parseError , type IParsedError } from '@microsoft/vscode-azext-utils' ;
7
7
import * as path from "path" ;
8
8
import { type Progress } from 'vscode' ;
9
9
import { ConnectionKey , DurableBackend , hostFileName , ProjectLanguage } from '../../../constants' ;
10
10
import { viewOutput } from '../../../constants-nls' ;
11
11
import { 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' ;
13
13
import { MismatchBehavior , setLocalAppSetting } from '../../../funcConfig/local.settings' ;
14
14
import { localize } from '../../../localize' ;
15
+ import { cpUtils } from '../../../utils/cpUtils' ;
15
16
import { durableUtils } from '../../../utils/durableUtils' ;
17
+ import { pythonUtils } from '../../../utils/pythonUtils' ;
18
+ import { venvUtils } from '../../../utils/venvUtils' ;
16
19
import { type IFunctionWizardContext } from '../IFunctionWizardContext' ;
17
20
18
21
export class DurableProjectConfigureStep < T extends IFunctionWizardContext > extends AzureWizardExecuteStepWithActivityOutput < T > {
@@ -25,22 +28,22 @@ export class DurableProjectConfigureStep<T extends IFunctionWizardContext> exten
25
28
protected getOutputLogFail ( _context : T ) : string {
26
29
return localize ( 'failedToConfigureDurableProject' , 'Failed to configure durable project settings.' ) ;
27
30
}
28
- protected getOutputLogProgress ( _context : T ) : string {
29
- return localize ( 'configuringDurableProject' , 'Configuring durable project settings...' ) ;
30
- }
31
31
protected preDeployTask : string = 'funcHostStart' ;
32
32
public stepName : string = 'DurableProjectConfigureStep' ;
33
33
public priority : number = 225 ;
34
34
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...' ) } ) ;
36
37
await this . configureHostAndLocalSettingsJson ( context ) ;
37
- await durableUtils . tryInstallDurableDependencies ( context ) ;
38
+ await this . tryInstallDurableDependencies ( context ) ;
38
39
}
39
40
40
41
public shouldExecute ( context : T ) : boolean {
41
42
return ! ! context . newDurableStorageType ;
42
43
}
43
44
45
+ // #region Durable Task Local Settings
46
+
44
47
private async configureHostAndLocalSettingsJson ( context : T ) : Promise < void > {
45
48
const hostJsonPath : string = path . join ( context . projectPath , hostFileName ) ;
46
49
@@ -65,15 +68,15 @@ export class DurableProjectConfigureStep<T extends IFunctionWizardContext> exten
65
68
66
69
switch ( context . newDurableStorageType ) {
67
70
case DurableBackend . Storage :
68
- hostJson . extensions . durableTask = durableUtils . getDefaultStorageTaskConfig ( ) ;
71
+ hostJson . extensions . durableTask = this . getDefaultStorageTaskConfig ( ) ;
69
72
// Omit setting azureWebJobsStorage since it should already be initialized during 'createNewProject'
70
73
break ;
71
74
case DurableBackend . Netherite :
72
- hostJson . extensions . durableTask = durableUtils . getDefaultNetheriteTaskConfig ( ) ;
75
+ hostJson . extensions . durableTask = this . getDefaultNetheriteTaskConfig ( ) ;
73
76
await setLocalAppSetting ( context , context . projectPath , ConnectionKey . EventHubs , '' , MismatchBehavior . Overwrite ) ;
74
77
break ;
75
78
case DurableBackend . DTS :
76
- hostJson . extensions . durableTask = durableUtils . getDefaultDTSTaskConfig ( ) ;
79
+ hostJson . extensions . durableTask = this . getDefaultDTSTaskConfig ( ) ;
77
80
// Non- .NET projects require a special preview extension bundle to work properly
78
81
// Todo: Remove once this functionality is out of preview
79
82
if ( context . language !== ProjectLanguage . CSharp && context . language !== ProjectLanguage . FSharp ) {
@@ -84,15 +87,147 @@ export class DurableProjectConfigureStep<T extends IFunctionWizardContext> exten
84
87
ext . outputChannel . appendLog ( localize ( 'extensionBundlePreview' , 'Updated "host.json" extension bundle to preview version to enable new DTS features.' ) ) ;
85
88
}
86
89
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 ) ;
88
91
break ;
89
92
case DurableBackend . SQL :
90
- hostJson . extensions . durableTask = durableUtils . getDefaultSqlTaskConfig ( ) ;
93
+ hostJson . extensions . durableTask = this . getDefaultSqlTaskConfig ( ) ;
91
94
await setLocalAppSetting ( context , context . projectPath , ConnectionKey . SQL , '' , MismatchBehavior . Overwrite ) ;
92
95
break ;
93
96
default :
94
97
}
95
98
96
99
await AzExtFsExtra . writeJSON ( hostJsonPath , hostJson ) ;
97
100
}
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
98
233
}
0 commit comments