From 63152f32ac091f04a398de1ae24af9761a559666 Mon Sep 17 00:00:00 2001 From: Malhar Khimsaria <96malhar@gmail.com> Date: Sun, 22 Sep 2024 21:09:52 -0700 Subject: [PATCH] fix: Impose a 127 character limit on the Lambda function handler when the package type is set to zip (#1812) Addresses https://github.com/aws/aws-lambda-dotnet/issues/1778 --- .../Diagnostics/AnalyzerReleases.Shipped.md | 7 ++++++ .../Diagnostics/DiagnosticDescriptors.cs | 7 ++++++ .../Validation/LambdaFunctionValidator.cs | 9 +++++++ .../ServerlessTemplates/sqsEvents.template | 22 ++++++++++------ .../SourceGeneratorTests.cs | 25 +++++++++++++++++++ .../ExceededMaximumHandlerLength.cs.error | 15 +++++++++++ .../InvalidSQSEvents.cs.error | 20 +++++++-------- .../SQSEventExamples/ValidSQSEvents.cs.txt | 4 +-- .../TestServerlessApp/serverless.template | 12 ++++----- 9 files changed, 95 insertions(+), 26 deletions(-) create mode 100644 Libraries/test/TestServerlessApp/ExceededMaximumHandlerLength.cs.error diff --git a/Libraries/src/Amazon.Lambda.Annotations.SourceGenerator/Diagnostics/AnalyzerReleases.Shipped.md b/Libraries/src/Amazon.Lambda.Annotations.SourceGenerator/Diagnostics/AnalyzerReleases.Shipped.md index f664c3c35..5e940e829 100644 --- a/Libraries/src/Amazon.Lambda.Annotations.SourceGenerator/Diagnostics/AnalyzerReleases.Shipped.md +++ b/Libraries/src/Amazon.Lambda.Annotations.SourceGenerator/Diagnostics/AnalyzerReleases.Shipped.md @@ -1,6 +1,13 @@ ; Shipped analyzer releases ; https://github.com/dotnet/roslyn-analyzers/blob/master/src/Microsoft.CodeAnalysis.Analyzers/ReleaseTrackingAnalyzers.Help.md +## Release 1.5.1 +### New Rules + +Rule ID | Category | Severity | Notes +--------|----------|----------|------- +AWSLambda0118 | AWSLambdaCSharpGenerator | Error | Maximum Handler Length Exceeded + ## Release 1.5.0 ### New Rules diff --git a/Libraries/src/Amazon.Lambda.Annotations.SourceGenerator/Diagnostics/DiagnosticDescriptors.cs b/Libraries/src/Amazon.Lambda.Annotations.SourceGenerator/Diagnostics/DiagnosticDescriptors.cs index fdf0813ec..f3561f433 100644 --- a/Libraries/src/Amazon.Lambda.Annotations.SourceGenerator/Diagnostics/DiagnosticDescriptors.cs +++ b/Libraries/src/Amazon.Lambda.Annotations.SourceGenerator/Diagnostics/DiagnosticDescriptors.cs @@ -138,5 +138,12 @@ public static class DiagnosticDescriptors category: "AWSLambdaCSharpGenerator", DiagnosticSeverity.Error, isEnabledByDefault: true); + + public static readonly DiagnosticDescriptor MaximumHandlerLengthExceeded = new DiagnosticDescriptor(id: "AWSLambda0118", + title: "Maximum Handler Length Exceeded", + messageFormat: "The handler string '{0}' exceeds the maximum length of 127 characters. Please trim down your project namespace, class name or method name to stay within the character limit.", + category: "AWSLambdaCSharpGenerator", + DiagnosticSeverity.Error, + isEnabledByDefault: true); } } \ No newline at end of file diff --git a/Libraries/src/Amazon.Lambda.Annotations.SourceGenerator/Validation/LambdaFunctionValidator.cs b/Libraries/src/Amazon.Lambda.Annotations.SourceGenerator/Validation/LambdaFunctionValidator.cs index f8e31c0b0..8b305e7cf 100644 --- a/Libraries/src/Amazon.Lambda.Annotations.SourceGenerator/Validation/LambdaFunctionValidator.cs +++ b/Libraries/src/Amazon.Lambda.Annotations.SourceGenerator/Validation/LambdaFunctionValidator.cs @@ -28,6 +28,15 @@ internal static bool ValidateFunction(GeneratorExecutionContext context, IMethod diagnostics.Add(Diagnostic.Create(DiagnosticDescriptors.InvalidResourceName, methodLocation)); } + // Check the handler length does not exceed 127 characters when the package type is set to zip + // The official AWS docs state a 128 character limit on the Lambda handler. However, there is an open issue where the last character is stripped off + // when the handler is exactly 128 characters long. Hence, we are enforcing a 127 character limit. + // https://github.com/aws/aws-lambda-dotnet/issues/1642 + if (lambdaFunctionModel.PackageType == LambdaPackageType.Zip && lambdaFunctionModel.Handler.Length > 127) + { + diagnostics.Add(Diagnostic.Create(DiagnosticDescriptors.MaximumHandlerLengthExceeded, methodLocation, lambdaFunctionModel.Handler)); + } + // Check for Serializer attribute if (!lambdaMethodSymbol.ContainingAssembly.HasAttribute(context, TypeFullNames.LambdaSerializerAttribute)) { diff --git a/Libraries/test/Amazon.Lambda.Annotations.SourceGenerators.Tests/Snapshots/ServerlessTemplates/sqsEvents.template b/Libraries/test/Amazon.Lambda.Annotations.SourceGenerators.Tests/Snapshots/ServerlessTemplates/sqsEvents.template index 7d7f0591a..13a56ed27 100644 --- a/Libraries/test/Amazon.Lambda.Annotations.SourceGenerators.Tests/Snapshots/ServerlessTemplates/sqsEvents.template +++ b/Libraries/test/Amazon.Lambda.Annotations.SourceGenerators.Tests/Snapshots/ServerlessTemplates/sqsEvents.template @@ -35,15 +35,18 @@ } }, "Properties": { - "Runtime": "dotnet6", - "CodeUri": ".", "MemorySize": 512, "Timeout": 30, "Policies": [ "AWSLambdaBasicExecutionRole" ], - "PackageType": "Zip", - "Handler": "TestProject::TestServerlessApp.SQSEventExamples.ValidSQSEvents_ProcessMessages_Generated::ProcessMessages", + "PackageType": "Image", + "ImageUri": ".", + "ImageConfig": { + "Command": [ + "TestProject::TestServerlessApp.SQSEventExamples.ValidSQSEvents_ProcessMessages_Generated::ProcessMessages" + ] + }, "Events": { "queue1": { "Type": "SQS", @@ -109,15 +112,18 @@ } }, "Properties": { - "Runtime": "dotnet6", - "CodeUri": ".", "MemorySize": 512, "Timeout": 30, "Policies": [ "AWSLambdaBasicExecutionRole" ], - "PackageType": "Zip", - "Handler": "TestProject::TestServerlessApp.SQSEventExamples.ValidSQSEvents_ProcessMessagesWithBatchFailureReporting_Generated::ProcessMessagesWithBatchFailureReporting", + "PackageType": "Image", + "ImageUri": ".", + "ImageConfig": { + "Command": [ + "TestProject::TestServerlessApp.SQSEventExamples.ValidSQSEvents_ProcessMessagesWithBatchFailureReporting_Generated::ProcessMessagesWithBatchFailureReporting" + ] + }, "Events": { "queue3": { "Type": "SQS", diff --git a/Libraries/test/Amazon.Lambda.Annotations.SourceGenerators.Tests/SourceGeneratorTests.cs b/Libraries/test/Amazon.Lambda.Annotations.SourceGenerators.Tests/SourceGeneratorTests.cs index bcde6c26e..5940acf89 100644 --- a/Libraries/test/Amazon.Lambda.Annotations.SourceGenerators.Tests/SourceGeneratorTests.cs +++ b/Libraries/test/Amazon.Lambda.Annotations.SourceGenerators.Tests/SourceGeneratorTests.cs @@ -1330,6 +1330,31 @@ public async Task VerifyValidSQSEvents() }.RunAsync(); } + [Fact] + public async Task ExceededMaximumHandlerLength() + { + await new VerifyCS.Test + { + TestState = + { + Sources = + { + (Path.Combine("TestServerlessApp", "PlaceholderClass.cs"), await File.ReadAllTextAsync(Path.Combine("TestServerlessApp", "PlaceholderClass.cs"))), + (Path.Combine("TestServerlessApp", "ExceededMaximumHandlerLength.cs"), await File.ReadAllTextAsync(Path.Combine("TestServerlessApp", "ExceededMaximumHandlerLength.cs.error"))), + (Path.Combine("Amazon.Lambda.Annotations", "LambdaFunctionAttribute.cs"), await File.ReadAllTextAsync(Path.Combine("Amazon.Lambda.Annotations", "LambdaFunctionAttribute.cs"))), + (Path.Combine("TestServerlessApp", "AssemblyAttributes.cs"), await File.ReadAllTextAsync(Path.Combine("TestServerlessApp", "AssemblyAttributes.cs"))), + }, + ExpectedDiagnostics = + { + DiagnosticResult + .CompilerError("AWSLambda0118") + .WithSpan($"TestServerlessApp{Path.DirectorySeparatorChar}ExceededMaximumHandlerLength.cs", 9, 9, 13, 10) + .WithArguments("TestProject::TestServerlessApp.ExceededMaximumHandlerLength_SayHelloXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX_Generated::SayHelloXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX") + }, + } + }.RunAsync(); + } + public void Dispose() { File.Delete(Path.Combine("TestServerlessApp", "serverless.template")); diff --git a/Libraries/test/TestServerlessApp/ExceededMaximumHandlerLength.cs.error b/Libraries/test/TestServerlessApp/ExceededMaximumHandlerLength.cs.error new file mode 100644 index 000000000..706041dde --- /dev/null +++ b/Libraries/test/TestServerlessApp/ExceededMaximumHandlerLength.cs.error @@ -0,0 +1,15 @@ +using System; +using Amazon.Lambda.Annotations; + +namespace TestServerlessApp +{ + public class ExceededMaximumHandlerLength + { + // This fails because generated handler is longer than 127 characters + [LambdaFunction] + public string SayHelloXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX() + { + return "Hello, World!"; + } + } +} \ No newline at end of file diff --git a/Libraries/test/TestServerlessApp/SQSEventExamples/InvalidSQSEvents.cs.error b/Libraries/test/TestServerlessApp/SQSEventExamples/InvalidSQSEvents.cs.error index 0b90371a7..1603f121d 100644 --- a/Libraries/test/TestServerlessApp/SQSEventExamples/InvalidSQSEvents.cs.error +++ b/Libraries/test/TestServerlessApp/SQSEventExamples/InvalidSQSEvents.cs.error @@ -12,21 +12,21 @@ namespace TestServerlessApp.SQSEventExamples public class InvalidSQSEvents { - [LambdaFunction] + [LambdaFunction(PackageType = LambdaPackageType.Image)] [SQSEvent("@testQueue", BatchSize = 0, MaximumBatchingWindowInSeconds = 302, MaximumConcurrency = 1)] public void ProcessMessageWithInvalidSQSEventAttributes(SQSEvent evnt) { Console.WriteLine($"Event processed: {evnt}"); } - [LambdaFunction] + [LambdaFunction(PackageType = LambdaPackageType.Image)] [SQSEvent("@testQueue")] public void ProcessMessageWithInvalidParameters(SQSEvent evnt, bool invalidParameter1, int invalidParameter2) { Console.WriteLine($"Event processed: {evnt}"); } - [LambdaFunction] + [LambdaFunction(PackageType = LambdaPackageType.Image)] [SQSEvent("@testQueue")] public bool ProcessMessageWithInvalidReturnType(SQSEvent evnt) { @@ -34,7 +34,7 @@ namespace TestServerlessApp.SQSEventExamples return true; } - [LambdaFunction] + [LambdaFunction(PackageType = LambdaPackageType.Image)] [RestApi(LambdaHttpMethod.Get, "/")] [SQSEvent("@testQueue")] public void ProcessMessageWithMultipleEventTypes(SQSEvent evnt) @@ -42,42 +42,42 @@ namespace TestServerlessApp.SQSEventExamples Console.WriteLine($"Event processed: {evnt}"); } - [LambdaFunction] + [LambdaFunction(PackageType = LambdaPackageType.Image)] [SQSEvent("test-queue")] public void ProcessMessageWithInvalidQueueArn(SQSEvent evnt) { Console.WriteLine($"Event processed: {evnt}"); } - [LambdaFunction] + [LambdaFunction(PackageType = LambdaPackageType.Image)] [SQSEvent("@testQueue", ResourceName = "sqs-event-source")] public void ProcessMessageWithInvalidResourceName(SQSEvent evnt) { Console.WriteLine($"Event processed: {evnt}"); } - [LambdaFunction] + [LambdaFunction(PackageType = LambdaPackageType.Image)] [SQSEvent("@testQueue", ResourceName = "")] public void ProcessMessageWithEmptyResourceName(SQSEvent evnt) { Console.WriteLine($"Event processed: {evnt}"); } - [LambdaFunction] + [LambdaFunction(PackageType = LambdaPackageType.Image)] [SQSEvent("@testQueue", BatchSize = 100)] public void ProcessMessageWithMaximumBatchingWindowInSecondsNotSpecified(SQSEvent evnt) { Console.WriteLine($"Event processed: {evnt}"); } - [LambdaFunction] + [LambdaFunction(PackageType = LambdaPackageType.Image)] [SQSEvent("@testQueue", BatchSize = 100, MaximumBatchingWindowInSeconds = 0)] public void ProcessMessageWithMaximumBatchingWindowInSecondsLessThanOne(SQSEvent evnt) { Console.WriteLine($"Event processed: {evnt}"); } - [LambdaFunction] + [LambdaFunction(PackageType = LambdaPackageType.Image)] [SQSEvent("arn:aws:sqs:us-east-2:444455556666:test-queue.fifo", BatchSize = 100, MaximumBatchingWindowInSeconds = 5)] public void ProcessMessageWithFifoQueue(SQSEvent evnt) { diff --git a/Libraries/test/TestServerlessApp/SQSEventExamples/ValidSQSEvents.cs.txt b/Libraries/test/TestServerlessApp/SQSEventExamples/ValidSQSEvents.cs.txt index 28327c33f..791a6614a 100644 --- a/Libraries/test/TestServerlessApp/SQSEventExamples/ValidSQSEvents.cs.txt +++ b/Libraries/test/TestServerlessApp/SQSEventExamples/ValidSQSEvents.cs.txt @@ -12,7 +12,7 @@ namespace TestServerlessApp.SQSEventExamples public class ValidSQSEvents { - [LambdaFunction] + [LambdaFunction(PackageType = LambdaPackageType.Image)] [SQSEvent("arn:aws:sqs:us-east-2:444455556666:queue1", BatchSize = 50, MaximumBatchingWindowInSeconds = 2, MaximumConcurrency = 30, Filters = "My-Filter-1; My-Filter-2")] [SQSEvent("arn:aws:sqs:us-east-2:444455556666:queue2", MaximumBatchingWindowInSeconds = 5, Enabled = false)] [SQSEvent("arn:aws:sqs:us-east-2:444455556666:my-queue")] @@ -22,7 +22,7 @@ namespace TestServerlessApp.SQSEventExamples Console.WriteLine($"Event processed: {evnt}"); } - [LambdaFunction] + [LambdaFunction(PackageType = LambdaPackageType.Image)] [SQSEvent("arn:aws:sqs:us-east-2:444455556666:queue3")] public async Task ProcessMessagesWithBatchFailureReporting(SQSEvent evnt) { diff --git a/Libraries/test/TestServerlessApp/serverless.template b/Libraries/test/TestServerlessApp/serverless.template index 969ae9c9f..8f938cfe1 100644 --- a/Libraries/test/TestServerlessApp/serverless.template +++ b/Libraries/test/TestServerlessApp/serverless.template @@ -913,12 +913,6 @@ "TestQueueEvent": { "Type": "SQS", "Properties": { - "Queue": { - "Fn::GetAtt": [ - "TestQueue", - "Arn" - ] - }, "BatchSize": 50, "FilterCriteria": { "Filters": [ @@ -933,6 +927,12 @@ "MaximumBatchingWindowInSeconds": 5, "ScalingConfig": { "MaximumConcurrency": 5 + }, + "Queue": { + "Fn::GetAtt": [ + "TestQueue", + "Arn" + ] } } }