1
1
using System ;
2
2
using System . Collections . Generic ;
3
3
using System . IO ;
4
+ using System . IO . Abstractions ;
4
5
using System . Linq ;
6
+ using System . Runtime . CompilerServices ;
5
7
using System . Text . RegularExpressions ;
6
8
using System . Threading . Tasks ;
7
9
using Azure . Functions . Cli . Common ;
8
10
using Azure . Functions . Cli . ExtensionBundle ;
11
+ using Azure . Functions . Cli . Extensions ;
9
12
using Azure . Functions . Cli . Helpers ;
10
13
using Azure . Functions . Cli . Interfaces ;
11
14
using Colors . Net ;
12
15
using Fclp ;
16
+ using ImTools ;
17
+ using Microsoft . Azure . AppService . Proxy . Common . Context ;
13
18
using Microsoft . Azure . WebJobs . Extensions . Http ;
19
+ using Microsoft . Azure . WebJobs . Script ;
14
20
using Newtonsoft . Json ;
15
21
using Newtonsoft . Json . Linq ;
16
22
using static Azure . Functions . Cli . Common . Constants ;
@@ -26,8 +32,9 @@ internal class CreateFunctionAction : BaseAction
26
32
private ITemplatesManager _templatesManager ;
27
33
private readonly ISecretsManager _secretsManager ;
28
34
private readonly IContextHelpManager _contextHelpManager ;
29
-
35
+ private readonly IUserInputHandler _userInputHandler ;
30
36
private readonly InitAction _initAction ;
37
+ Lazy < IEnumerable < UserPrompt > > _userPrompts ;
31
38
public WorkerRuntime workerRuntime ;
32
39
33
40
public string Language { get ; set ; }
@@ -39,15 +46,18 @@ internal class CreateFunctionAction : BaseAction
39
46
public AuthorizationLevel ? AuthorizationLevel { get ; set ; }
40
47
41
48
Lazy < IEnumerable < Template > > _templates ;
42
-
49
+ Lazy < IEnumerable < NewTemplate > > _newTemplates ;
43
50
44
51
public CreateFunctionAction ( ITemplatesManager templatesManager , ISecretsManager secretsManager , IContextHelpManager contextHelpManager )
45
52
{
46
53
_templatesManager = templatesManager ;
47
54
_secretsManager = secretsManager ;
48
55
_contextHelpManager = contextHelpManager ;
49
56
_initAction = new InitAction ( _templatesManager , _secretsManager ) ;
57
+ _userInputHandler = new UserInputHandler ( _templatesManager ) ;
50
58
_templates = new Lazy < IEnumerable < Template > > ( ( ) => { return _templatesManager . Templates . Result ; } ) ;
59
+ _newTemplates = new Lazy < IEnumerable < NewTemplate > > ( ( ) => { return _templatesManager . NewTemplates . Result ; } ) ;
60
+ _userPrompts = new Lazy < IEnumerable < UserPrompt > > ( ( ) => { return _templatesManager . UserPrompts . Result ; } ) ;
51
61
}
52
62
53
63
public override ICommandLineParserResult ParseArgs ( string [ ] args )
@@ -104,17 +114,6 @@ public async override Task RunAsync()
104
114
105
115
await UpdateLanguageAndRuntime ( ) ;
106
116
107
- // Check if the programming model is PyStein
108
- if ( IsNewPythonProgrammingModel ( ) )
109
- {
110
- // TODO: Remove these messages once creating new functions in the new programming model is supported
111
- ColoredConsole . WriteLine ( WarningColor ( "When using the new Python programming model, triggers and bindings are created as decorators within the Python file itself." ) ) ;
112
- ColoredConsole . Write ( AdditionalInfoColor ( "For information on how to create a new function with the new programming model, see " ) ) ;
113
- PythonHelpers . PrintPySteinWikiLink ( ) ;
114
- throw new CliException (
115
- "Function not created! 'func new' is not supported for the preview of the V2 Python programming model." ) ;
116
- }
117
-
118
117
if ( WorkerRuntimeLanguageHelper . IsDotnet ( workerRuntime ) && ! Csx )
119
118
{
120
119
if ( string . IsNullOrWhiteSpace ( TemplateName ) )
@@ -133,6 +132,55 @@ public async override Task RunAsync()
133
132
var namespaceStr = Path . GetFileName ( Environment . CurrentDirectory ) ;
134
133
await DotnetHelpers . DeployDotnetFunction ( TemplateName . Replace ( " " , string . Empty ) , Utilities . SanitizeClassName ( FunctionName ) , Utilities . SanitizeNameSpace ( namespaceStr ) , Language . Replace ( "-isolated" , "" ) , workerRuntime , AuthorizationLevel ) ;
135
134
}
135
+ else if ( IsNewPythonProgrammingModel ( ) )
136
+ {
137
+ if ( string . IsNullOrEmpty ( TemplateName ) )
138
+ {
139
+ SelectionMenuHelper . DisplaySelectionWizardPrompt ( "template" ) ;
140
+ TemplateName = TemplateName ?? SelectionMenuHelper . DisplaySelectionWizard ( GetTriggerNamesFromNewTemplates ( Language ) ) ;
141
+ }
142
+
143
+ if ( string . IsNullOrEmpty ( FileName ) )
144
+ {
145
+ var userPrompt = _userPrompts . Value . First ( x => string . Equals ( x . Id , "app-selectedFileName" , StringComparison . OrdinalIgnoreCase ) ) ;
146
+ while ( ! _userInputHandler . ValidateResponse ( userPrompt , FileName ) )
147
+ {
148
+ _userInputHandler . PrintInputLabel ( userPrompt , PySteinFunctionAppPy ) ;
149
+ FileName = Console . ReadLine ( ) ;
150
+ if ( string . IsNullOrEmpty ( FileName ) )
151
+ {
152
+ FileName = PySteinFunctionAppPy ;
153
+ }
154
+ }
155
+ }
156
+
157
+ var variables = new Dictionary < string , string > ( ) ;
158
+ var jobName = "appendToFile" ;
159
+ if ( FileName != PySteinFunctionAppPy )
160
+ {
161
+ var filePath = Path . Combine ( Environment . CurrentDirectory , FileName ) ;
162
+ jobName = ! FileUtility . FileExists ( filePath ) ? "CreateNewBlueprint" : "AppendToBlueprint" ;
163
+ variables [ "$(BLUEPRINT_FILENAME)" ] = FileName ;
164
+ FileName = FileName [ ..^ Path . GetExtension ( FileName ) . Length ] ;
165
+ }
166
+ else
167
+ {
168
+ variables [ "$(SELECTED_FILEPATH)" ] = FileName ;
169
+ }
170
+
171
+ var template = _newTemplates . Value . FirstOrDefault ( t => string . Equals ( t . Name , TemplateName , StringComparison . CurrentCultureIgnoreCase ) && string . Equals ( t . Language , Language , StringComparison . CurrentCultureIgnoreCase ) ) ;
172
+ var templateJob = template . Jobs . Single ( x => x . Type . Equals ( jobName , StringComparison . OrdinalIgnoreCase ) ) ;
173
+ var providedInputs = new Dictionary < string , string > ( ) { { GetFunctionNameParamId , FunctionName } } ;
174
+
175
+ _userInputHandler . RunUserInputActions ( providedInputs , templateJob . Inputs , variables ) ;
176
+
177
+ if ( string . IsNullOrEmpty ( FunctionName ) )
178
+ {
179
+ FunctionName = providedInputs [ GetFunctionNameParamId ] ;
180
+ }
181
+
182
+ await _templatesManager . Deploy ( templateJob , template , variables ) ;
183
+ }
136
184
else
137
185
{
138
186
SelectionMenuHelper . DisplaySelectionWizardPrompt ( "template" ) ;
@@ -276,7 +324,8 @@ private IEnumerable<string> GetTriggerNames(string templateLanguage, bool forNew
276
324
277
325
private IEnumerable < Template > GetLanguageTemplates ( string templateLanguage , bool forNewModelHelp = false )
278
326
{
279
- if ( IsNewNodeJsProgrammingModel ( workerRuntime ) || ( forNewModelHelp && ( Language == Languages . TypeScript || Language == Languages . JavaScript ) ) )
327
+ if ( IsNewNodeJsProgrammingModel ( workerRuntime ) ||
328
+ ( forNewModelHelp && ( Languages . TypeScript . EqualsIgnoreCase ( templateLanguage ) || Languages . JavaScript . EqualsIgnoreCase ( templateLanguage ) ) ) )
280
329
{
281
330
return _templates . Value . Where ( t => t . Id . EndsWith ( "-4.x" ) && t . Metadata . Language . Equals ( templateLanguage , StringComparison . OrdinalIgnoreCase ) ) ;
282
331
}
@@ -289,6 +338,21 @@ private IEnumerable<Template> GetLanguageTemplates(string templateLanguage, bool
289
338
return _templates . Value . Where ( t => t . Metadata . Language . Equals ( templateLanguage , StringComparison . OrdinalIgnoreCase ) ) ;
290
339
}
291
340
341
+ private IEnumerable < string > GetTriggerNamesFromNewTemplates ( string templateLanguage , bool forNewModelHelp = false )
342
+ {
343
+ return GetNewTemplates ( templateLanguage , forNewModelHelp ) . Select ( t => t . Name ) . Distinct ( ) ;
344
+ }
345
+
346
+ private IEnumerable < NewTemplate > GetNewTemplates ( string templateLanguage , bool forNewModelHelp = false )
347
+ {
348
+ if ( IsNewPythonProgrammingModel ( ) || ( Languages . Python . EqualsIgnoreCase ( templateLanguage ) && forNewModelHelp ) )
349
+ {
350
+ return _newTemplates . Value . Where ( t => t . Language . Equals ( templateLanguage , StringComparison . OrdinalIgnoreCase ) ) ;
351
+ }
352
+
353
+ throw new CliException ( "The new version of templates are only supported for Python." ) ;
354
+ }
355
+
292
356
private void ConfigureAuthorizationLevel ( Template template )
293
357
{
294
358
var bindings = template . Function [ "bindings" ] ;
@@ -366,7 +430,7 @@ public async Task<bool> ProcessHelpRequest(string triggerName, bool promptQuesti
366
430
return false ;
367
431
}
368
432
369
- var supportedLanguages = new List < string > ( ) { Languages . JavaScript , Languages . TypeScript } ;
433
+ var supportedLanguages = new List < string > ( ) { Languages . JavaScript , Languages . TypeScript , Languages . Python } ;
370
434
if ( string . IsNullOrEmpty ( Language ) )
371
435
{
372
436
if ( CurrentPathHasLocalSettings ( ) )
@@ -386,8 +450,18 @@ public async Task<bool> ProcessHelpRequest(string triggerName, bool promptQuesti
386
450
}
387
451
}
388
452
389
- var triggerNames = GetTriggerNames ( Language , forNewModelHelp : true ) ;
453
+ IEnumerable < string > triggerNames ;
454
+ if ( Languages . Python . EqualsIgnoreCase ( Language ) )
455
+ {
456
+ triggerNames = GetTriggerNamesFromNewTemplates ( Language , forNewModelHelp : true ) ;
457
+ }
458
+ else
459
+ {
460
+ triggerNames = GetTriggerNames ( Language , forNewModelHelp : true ) ;
461
+ }
462
+
390
463
await _contextHelpManager . LoadTriggerHelp ( Language , triggerNames . ToList ( ) ) ;
464
+
391
465
if ( _contextHelpManager . IsValidTriggerNameForHelp ( triggerName ) )
392
466
{
393
467
triggerName = _contextHelpManager . GetTriggerTypeFromTriggerNameForHelp ( triggerName ) ;
@@ -445,4 +519,4 @@ private bool CurrentPathHasLocalSettings()
445
519
return FileSystemHelpers . FileExists ( Path . Combine ( Environment . CurrentDirectory , "local.settings.json" ) ) ;
446
520
}
447
521
}
448
- }
522
+ }
0 commit comments