2
2
using Amazon . Lambda . Annotations . SourceGenerator . Extensions ;
3
3
using Amazon . Lambda . Annotations . SourceGenerator . FileIO ;
4
4
using Amazon . Lambda . Annotations . SourceGenerator . Models ;
5
- using Amazon . Lambda . Annotations . SourceGenerator . Models . Attributes ;
6
5
using Amazon . Lambda . Annotations . SourceGenerator . Templates ;
7
6
using Amazon . Lambda . Annotations . SourceGenerator . Writers ;
8
7
using Microsoft . CodeAnalysis ;
9
- using Microsoft . CodeAnalysis . CSharp . Syntax ;
10
8
using Microsoft . CodeAnalysis . Text ;
11
9
using System ;
12
10
using System . Collections . Generic ;
13
- using System . Diagnostics ;
14
11
using System . Linq ;
15
12
using System . Text ;
16
- using System . Text . RegularExpressions ;
17
13
18
14
namespace Amazon . Lambda . Annotations . SourceGenerator
19
15
{
@@ -41,12 +37,6 @@ public class Generator : ISourceGenerator
41
37
"dotnet8"
42
38
} ;
43
39
44
- // Only allow alphanumeric characters
45
- private readonly Regex _resourceNameRegex = new Regex ( "^[a-zA-Z0-9]+$" ) ;
46
-
47
- // Regex for the 'Name' property for API Gateway attributes - https://docs.aws.amazon.com/apigateway/latest/developerguide/request-response-data-mappings.html
48
- private readonly Regex _parameterAttributeNameRegex = new Regex ( "^[a-zA-Z0-9._$-]+$" ) ;
49
-
50
40
public Generator ( )
51
41
{
52
42
#if DEBUG
@@ -144,106 +134,46 @@ public void Execute(GeneratorExecutionContext context)
144
134
}
145
135
}
146
136
147
- var configureMethodModel = semanticModelProvider . GetConfigureMethodModel ( receiver . StartupClasses . FirstOrDefault ( ) ) ;
137
+ var configureMethodSymbol = semanticModelProvider . GetConfigureMethodModel ( receiver . StartupClasses . FirstOrDefault ( ) ) ;
148
138
149
139
var annotationReport = new AnnotationReport ( ) ;
150
140
151
141
var templateHandler = new CloudFormationTemplateHandler ( _fileManager , _directoryManager ) ;
152
142
153
143
var lambdaModels = new List < LambdaFunctionModel > ( ) ;
154
144
155
- foreach ( var lambdaMethod in receiver . LambdaMethods )
145
+ foreach ( var lambdaMethodDeclarationSyntax in receiver . LambdaMethods )
156
146
{
157
- var lambdaMethodModel = semanticModelProvider . GetMethodSemanticModel ( lambdaMethod ) ;
147
+ var lambdaMethodSymbol = semanticModelProvider . GetMethodSemanticModel ( lambdaMethodDeclarationSyntax ) ;
148
+ var lambdaMethodLocation = lambdaMethodDeclarationSyntax . GetLocation ( ) ;
158
149
159
- if ( ! HasSerializerAttribute ( context , lambdaMethodModel ) )
150
+ var lambdaFunctionModel = LambdaFunctionModelBuilder . BuildAndValidate ( lambdaMethodSymbol , lambdaMethodLocation , configureMethodSymbol , context , isExecutable , defaultRuntime , diagnosticReporter ) ;
151
+ if ( ! lambdaFunctionModel . IsValid )
160
152
{
161
- diagnosticReporter . Report ( Diagnostic . Create ( DiagnosticDescriptors . MissingLambdaSerializer ,
162
- lambdaMethod . GetLocation ( ) ) ) ;
163
-
153
+ // If the model is not valid then skip it from further processing
164
154
foundFatalError = true ;
165
155
continue ;
166
156
}
167
157
168
- // Check for necessary references
169
- if ( lambdaMethodModel . HasAttribute ( context , TypeFullNames . RestApiAttribute )
170
- || lambdaMethodModel . HasAttribute ( context , TypeFullNames . HttpApiAttribute ) )
171
- {
172
- // Check for arbitrary type from "Amazon.Lambda.APIGatewayEvents"
173
- if ( context . Compilation . ReferencedAssemblyNames . FirstOrDefault ( x => x . Name == "Amazon.Lambda.APIGatewayEvents" ) == null )
174
- {
175
- diagnosticReporter . Report ( Diagnostic . Create ( DiagnosticDescriptors . MissingDependencies ,
176
- lambdaMethod . GetLocation ( ) ,
177
- "Amazon.Lambda.APIGatewayEvents" ) ) ;
178
-
179
- foundFatalError = true ;
180
- continue ;
181
- }
182
- }
183
-
184
- var serializerInfo = GetSerializerInfoAttribute ( context , lambdaMethodModel ) ;
185
-
186
- var model = LambdaFunctionModelBuilder . Build ( lambdaMethodModel , configureMethodModel , context , isExecutable , serializerInfo , defaultRuntime ) ;
187
-
188
- // If there are more than one event, report them as errors
189
- if ( model . LambdaMethod . Events . Count > 1 )
190
- {
191
- foreach ( var attribute in lambdaMethodModel . GetAttributes ( ) . Where ( attribute => TypeFullNames . Events . Contains ( attribute . AttributeClass . ToDisplayString ( ) ) ) )
192
- {
193
- diagnosticReporter . Report ( Diagnostic . Create ( DiagnosticDescriptors . MultipleEventsNotSupported ,
194
- Location . Create ( attribute . ApplicationSyntaxReference . SyntaxTree , attribute . ApplicationSyntaxReference . Span ) ,
195
- DiagnosticSeverity . Error ) ) ;
196
- }
197
-
198
- foundFatalError = true ;
199
- // Skip multi-event lambda method from processing and check remaining lambda methods for diagnostics
200
- continue ;
201
- }
202
- if ( model . LambdaMethod . ReturnsIHttpResults && ! model . LambdaMethod . Events . Contains ( EventType . API ) )
203
- {
204
- diagnosticReporter . Report ( Diagnostic . Create ( DiagnosticDescriptors . HttpResultsOnNonApiFunction ,
205
- Location . Create ( lambdaMethod . SyntaxTree , lambdaMethod . Span ) ,
206
- DiagnosticSeverity . Error ) ) ;
207
-
208
- foundFatalError = true ;
209
- continue ;
210
- }
211
-
212
- if ( ! _resourceNameRegex . IsMatch ( model . ResourceName ) )
213
- {
214
- diagnosticReporter . Report ( Diagnostic . Create ( DiagnosticDescriptors . InvalidResourceName ,
215
- Location . Create ( lambdaMethod . SyntaxTree , lambdaMethod . Span ) ,
216
- DiagnosticSeverity . Error ) ) ;
217
-
218
- foundFatalError = true ;
219
- continue ;
220
- }
221
-
222
- if ( ! AreLambdaMethodParametersValid ( lambdaMethod , model , diagnosticReporter ) )
223
- {
224
- foundFatalError = true ;
225
- continue ;
226
- }
227
-
228
- var template = new LambdaFunctionTemplate ( model ) ;
158
+ var template = new LambdaFunctionTemplate ( lambdaFunctionModel ) ;
229
159
230
160
string sourceText ;
231
161
try
232
162
{
233
163
sourceText = template . TransformText ( ) . ToEnvironmentLineEndings ( ) ;
234
- context . AddSource ( $ "{ model . GeneratedMethod . ContainingType . Name } .g.cs", SourceText . From ( sourceText , Encoding . UTF8 , SourceHashAlgorithm . Sha256 ) ) ;
164
+ context . AddSource ( $ "{ lambdaFunctionModel . GeneratedMethod . ContainingType . Name } .g.cs", SourceText . From ( sourceText , Encoding . UTF8 , SourceHashAlgorithm . Sha256 ) ) ;
235
165
}
236
166
catch ( Exception e ) when ( e is NotSupportedException || e is InvalidOperationException )
237
167
{
238
- diagnosticReporter . Report ( Diagnostic . Create ( DiagnosticDescriptors . CodeGenerationFailed , Location . Create ( lambdaMethod . SyntaxTree , lambdaMethod . Span ) , e . Message ) ) ;
168
+ diagnosticReporter . Report ( Diagnostic . Create ( DiagnosticDescriptors . CodeGenerationFailed , Location . Create ( lambdaMethodDeclarationSyntax . SyntaxTree , lambdaMethodDeclarationSyntax . Span ) , e . Message ) ) ;
239
169
return ;
240
170
}
241
171
242
172
// report every generated file to build output
243
- diagnosticReporter . Report ( Diagnostic . Create ( DiagnosticDescriptors . CodeGeneration , Location . None , $ "{ model . GeneratedMethod . ContainingType . Name } .g.cs", sourceText ) ) ;
173
+ diagnosticReporter . Report ( Diagnostic . Create ( DiagnosticDescriptors . CodeGeneration , Location . None , $ "{ lambdaFunctionModel . GeneratedMethod . ContainingType . Name } .g.cs", sourceText ) ) ;
244
174
245
- lambdaModels . Add ( model ) ;
246
- annotationReport . LambdaFunctions . Add ( model ) ;
175
+ lambdaModels . Add ( lambdaFunctionModel ) ;
176
+ annotationReport . LambdaFunctions . Add ( lambdaFunctionModel ) ;
247
177
}
248
178
249
179
if ( isExecutable )
@@ -348,110 +278,10 @@ private static ExecutableAssembly GenerateExecutableAssemblySource(
348
278
lambdaModels [ 0 ] . LambdaMethod . ContainingNamespace ) ;
349
279
}
350
280
351
- private bool HasSerializerAttribute ( GeneratorExecutionContext context , IMethodSymbol methodModel )
352
- {
353
- return methodModel . ContainingAssembly . HasAttribute ( context , TypeFullNames . LambdaSerializerAttribute ) ;
354
- }
355
-
356
- private LambdaSerializerInfo GetSerializerInfoAttribute ( GeneratorExecutionContext context , IMethodSymbol methodModel )
357
- {
358
- var serializerString = DEFAULT_LAMBDA_SERIALIZER ;
359
-
360
- ISymbol symbol = null ;
361
-
362
- // First check if method has the Lambda Serializer.
363
- if ( methodModel . HasAttribute (
364
- context ,
365
- TypeFullNames . LambdaSerializerAttribute ) )
366
- {
367
- symbol = methodModel ;
368
- }
369
- // Then check assembly
370
- else if ( methodModel . ContainingAssembly . HasAttribute (
371
- context ,
372
- TypeFullNames . LambdaSerializerAttribute ) )
373
- {
374
- symbol = methodModel . ContainingAssembly ;
375
- }
376
- // Else return the default serializer.
377
- else
378
- {
379
- return new LambdaSerializerInfo ( serializerString ) ;
380
- }
381
-
382
- var attribute = symbol . GetAttributes ( ) . FirstOrDefault ( attr => attr . AttributeClass . Name == TypeFullNames . LambdaSerializerAttributeWithoutNamespace ) ;
383
-
384
- var serializerValue = attribute . ConstructorArguments . FirstOrDefault ( kvp => kvp . Type . Name == nameof ( Type ) ) . Value ;
385
-
386
- if ( serializerValue != null )
387
- {
388
- serializerString = serializerValue . ToString ( ) ;
389
- }
390
-
391
- return new LambdaSerializerInfo ( serializerString ) ;
392
- }
393
-
394
281
public void Initialize ( GeneratorInitializationContext context )
395
282
{
396
283
// Register a syntax receiver that will be created for each generation pass
397
284
context . RegisterForSyntaxNotifications ( ( ) => new SyntaxReceiver ( _fileManager , _directoryManager ) ) ;
398
285
}
399
-
400
- private bool AreLambdaMethodParametersValid ( MethodDeclarationSyntax declarationSyntax , LambdaFunctionModel model , DiagnosticReporter diagnosticReporter )
401
- {
402
- var isValid = true ;
403
- foreach ( var parameter in model . LambdaMethod . Parameters )
404
- {
405
- if ( parameter . Attributes . Any ( att => att . Type . FullName == TypeFullNames . FromQueryAttribute ) )
406
- {
407
- var fromQueryAttribute = parameter . Attributes . First ( att => att . Type . FullName == TypeFullNames . FromQueryAttribute ) as AttributeModel < APIGateway . FromQueryAttribute > ;
408
- // Use parameter name as key, if Name has not specified explicitly in the attribute definition.
409
- var parameterKey = fromQueryAttribute ? . Data ? . Name ?? parameter . Name ;
410
-
411
- if ( ! parameter . Type . IsPrimitiveType ( ) && ! parameter . Type . IsPrimitiveEnumerableType ( ) )
412
- {
413
- isValid = false ;
414
- diagnosticReporter . Report ( Diagnostic . Create ( DiagnosticDescriptors . UnsupportedMethodParameterType ,
415
- Location . Create ( declarationSyntax . SyntaxTree , declarationSyntax . Span ) ,
416
- parameterKey , parameter . Type . FullName ) ) ;
417
- }
418
- }
419
-
420
- foreach ( var att in parameter . Attributes )
421
- {
422
- var parameterAttributeName = string . Empty ;
423
- switch ( att . Type . FullName )
424
- {
425
- case TypeFullNames . FromQueryAttribute :
426
- var fromQueryAttribute = ( AttributeModel < APIGateway . FromQueryAttribute > ) att ;
427
- parameterAttributeName = fromQueryAttribute . Data . Name ;
428
- break ;
429
-
430
- case TypeFullNames . FromRouteAttribute :
431
- var fromRouteAttribute = ( AttributeModel < APIGateway . FromRouteAttribute > ) att ;
432
- parameterAttributeName = fromRouteAttribute . Data . Name ;
433
- break ;
434
-
435
- case TypeFullNames . FromHeaderAttribute :
436
- var fromHeaderAttribute = ( AttributeModel < APIGateway . FromHeaderAttribute > ) att ;
437
- parameterAttributeName = fromHeaderAttribute . Data . Name ;
438
- break ;
439
-
440
- default :
441
- break ;
442
- }
443
-
444
- if ( ! string . IsNullOrEmpty ( parameterAttributeName ) && ! _parameterAttributeNameRegex . IsMatch ( parameterAttributeName ) )
445
- {
446
- isValid = false ;
447
- diagnosticReporter . Report ( Diagnostic . Create ( DiagnosticDescriptors . InvalidParameterAttributeName ,
448
- Location . Create ( declarationSyntax . SyntaxTree , declarationSyntax . Span ) ,
449
- parameterAttributeName , parameter . Name ) ) ;
450
- }
451
- }
452
- }
453
-
454
- return isValid ;
455
- }
456
286
}
457
287
}
0 commit comments