diff --git a/.autover/changes/add-scheduleevent-annotation.json b/.autover/changes/add-scheduleevent-annotation.json new file mode 100644 index 000000000..dc25a283f --- /dev/null +++ b/.autover/changes/add-scheduleevent-annotation.json @@ -0,0 +1,11 @@ +{ + "Projects": [ + { + "Name": "Amazon.Lambda.Annotations", + "Type": "Minor", + "ChangelogMessages": [ + "Added [ScheduleEvent] annotation attribute for declaratively configuring schedule-triggered Lambda functions with support for rate and cron expressions, description, input, and enabled state." + ] + } + ] +} diff --git a/Libraries/src/Amazon.Lambda.Annotations.SourceGenerator/Diagnostics/AnalyzerReleases.Unshipped.md b/Libraries/src/Amazon.Lambda.Annotations.SourceGenerator/Diagnostics/AnalyzerReleases.Unshipped.md index d1a9a89d0..e6cb14ac1 100644 --- a/Libraries/src/Amazon.Lambda.Annotations.SourceGenerator/Diagnostics/AnalyzerReleases.Unshipped.md +++ b/Libraries/src/Amazon.Lambda.Annotations.SourceGenerator/Diagnostics/AnalyzerReleases.Unshipped.md @@ -21,3 +21,4 @@ AWSLambda0133 | AWSLambdaCSharpGenerator | Error | ALB Listener Reference Not Fo AWSLambda0134 | AWSLambdaCSharpGenerator | Error | FromRoute not supported on ALB functions AWSLambda0135 | AWSLambdaCSharpGenerator | Error | Unmapped parameter on ALB function AWSLambda0136 | AWSLambdaCSharpGenerator | Error | Invalid S3EventAttribute +AWSLambda0139 | AWSLambdaCSharpGenerator | Error | Invalid ScheduleEventAttribute diff --git a/Libraries/src/Amazon.Lambda.Annotations.SourceGenerator/Diagnostics/DiagnosticDescriptors.cs b/Libraries/src/Amazon.Lambda.Annotations.SourceGenerator/Diagnostics/DiagnosticDescriptors.cs index e1a11087f..eae205078 100644 --- a/Libraries/src/Amazon.Lambda.Annotations.SourceGenerator/Diagnostics/DiagnosticDescriptors.cs +++ b/Libraries/src/Amazon.Lambda.Annotations.SourceGenerator/Diagnostics/DiagnosticDescriptors.cs @@ -281,5 +281,12 @@ public static class DiagnosticDescriptors category: "AWSLambdaCSharpGenerator", DiagnosticSeverity.Error, isEnabledByDefault: true); + + public static readonly DiagnosticDescriptor InvalidScheduleEventAttribute = new DiagnosticDescriptor(id: "AWSLambda0139", + title: "Invalid ScheduleEventAttribute", + messageFormat: "Invalid ScheduleEventAttribute encountered: {0}", + category: "AWSLambdaCSharpGenerator", + DiagnosticSeverity.Error, + isEnabledByDefault: true); } } diff --git a/Libraries/src/Amazon.Lambda.Annotations.SourceGenerator/Models/Attributes/AttributeModelBuilder.cs b/Libraries/src/Amazon.Lambda.Annotations.SourceGenerator/Models/Attributes/AttributeModelBuilder.cs index 0d1067bb6..ca00e8db1 100644 --- a/Libraries/src/Amazon.Lambda.Annotations.SourceGenerator/Models/Attributes/AttributeModelBuilder.cs +++ b/Libraries/src/Amazon.Lambda.Annotations.SourceGenerator/Models/Attributes/AttributeModelBuilder.cs @@ -1,5 +1,6 @@ using System; using Amazon.Lambda.Annotations.ALB; +using Amazon.Lambda.Annotations.Schedule; using Amazon.Lambda.Annotations.APIGateway; using Amazon.Lambda.Annotations.S3; using Amazon.Lambda.Annotations.SQS; @@ -101,6 +102,15 @@ public static AttributeModel Build(AttributeData att, GeneratorExecutionContext Type = TypeModelBuilder.Build(att.AttributeClass, context) }; } + else if (att.AttributeClass.Equals(context.Compilation.GetTypeByMetadataName(TypeFullNames.ScheduleEventAttribute), SymbolEqualityComparer.Default)) + { + var data = ScheduleEventAttributeBuilder.Build(att); + model = new AttributeModel + { + Data = data, + Type = TypeModelBuilder.Build(att.AttributeClass, context) + }; + } else if (att.AttributeClass.Equals(context.Compilation.GetTypeByMetadataName(TypeFullNames.HttpApiAuthorizerAttribute), SymbolEqualityComparer.Default)) { var data = HttpApiAuthorizerAttributeBuilder.Build(att); diff --git a/Libraries/src/Amazon.Lambda.Annotations.SourceGenerator/Models/Attributes/ScheduleEventAttributeBuilder.cs b/Libraries/src/Amazon.Lambda.Annotations.SourceGenerator/Models/Attributes/ScheduleEventAttributeBuilder.cs new file mode 100644 index 000000000..a7a03237d --- /dev/null +++ b/Libraries/src/Amazon.Lambda.Annotations.SourceGenerator/Models/Attributes/ScheduleEventAttributeBuilder.cs @@ -0,0 +1,44 @@ +using Amazon.Lambda.Annotations.Schedule; +using Microsoft.CodeAnalysis; +using System; + +namespace Amazon.Lambda.Annotations.SourceGenerator.Models.Attributes +{ + /// + /// Builder for . + /// + public class ScheduleEventAttributeBuilder + { + public static ScheduleEventAttribute Build(AttributeData att) + { + if (att.ConstructorArguments.Length != 1) + { + throw new NotSupportedException($"{TypeFullNames.ScheduleEventAttribute} must have constructor with 1 argument."); + } + var schedule = att.ConstructorArguments[0].Value as string; + var data = new ScheduleEventAttribute(schedule); + + foreach (var pair in att.NamedArguments) + { + if (pair.Key == nameof(data.ResourceName) && pair.Value.Value is string resourceName) + { + data.ResourceName = resourceName; + } + else if (pair.Key == nameof(data.Description) && pair.Value.Value is string description) + { + data.Description = description; + } + else if (pair.Key == nameof(data.Input) && pair.Value.Value is string input) + { + data.Input = input; + } + else if (pair.Key == nameof(data.Enabled) && pair.Value.Value is bool enabled) + { + data.Enabled = enabled; + } + } + + return data; + } + } +} diff --git a/Libraries/src/Amazon.Lambda.Annotations.SourceGenerator/Models/EventTypeBuilder.cs b/Libraries/src/Amazon.Lambda.Annotations.SourceGenerator/Models/EventTypeBuilder.cs index 3dfc51799..ca827b9c6 100644 --- a/Libraries/src/Amazon.Lambda.Annotations.SourceGenerator/Models/EventTypeBuilder.cs +++ b/Libraries/src/Amazon.Lambda.Annotations.SourceGenerator/Models/EventTypeBuilder.cs @@ -30,6 +30,10 @@ public static HashSet Build(IMethodSymbol lambdaMethodSymbol, { events.Add(EventType.S3); } + else if (attribute.AttributeClass.ToDisplayString() == TypeFullNames.ScheduleEventAttribute) + { + events.Add(EventType.Schedule); + } else if (attribute.AttributeClass.ToDisplayString() == TypeFullNames.HttpApiAuthorizerAttribute || attribute.AttributeClass.ToDisplayString() == TypeFullNames.RestApiAuthorizerAttribute) { diff --git a/Libraries/src/Amazon.Lambda.Annotations.SourceGenerator/SyntaxReceiver.cs b/Libraries/src/Amazon.Lambda.Annotations.SourceGenerator/SyntaxReceiver.cs index 2091d6c94..8198ecf0b 100644 --- a/Libraries/src/Amazon.Lambda.Annotations.SourceGenerator/SyntaxReceiver.cs +++ b/Libraries/src/Amazon.Lambda.Annotations.SourceGenerator/SyntaxReceiver.cs @@ -23,7 +23,8 @@ internal class SyntaxReceiver : ISyntaxContextReceiver { "RestApiAttribute", "RestApi" }, { "SQSEventAttribute", "SQSEvent" }, { "ALBApiAttribute", "ALBApi" }, - { "S3EventAttribute", "S3Event" } + { "S3EventAttribute", "S3Event" }, + { "ScheduleEventAttribute", "ScheduleEvent" } }; public List LambdaMethods { get; } = new List(); diff --git a/Libraries/src/Amazon.Lambda.Annotations.SourceGenerator/TypeFullNames.cs b/Libraries/src/Amazon.Lambda.Annotations.SourceGenerator/TypeFullNames.cs index 59fa1d830..2db1a8e45 100644 --- a/Libraries/src/Amazon.Lambda.Annotations.SourceGenerator/TypeFullNames.cs +++ b/Libraries/src/Amazon.Lambda.Annotations.SourceGenerator/TypeFullNames.cs @@ -56,6 +56,9 @@ public static class TypeFullNames public const string S3Event = "Amazon.Lambda.S3Events.S3Event"; public const string S3EventAttribute = "Amazon.Lambda.Annotations.S3.S3EventAttribute"; + public const string ScheduledEvent = "Amazon.Lambda.CloudWatchEvents.ScheduledEvents.ScheduledEvent"; + public const string ScheduleEventAttribute = "Amazon.Lambda.Annotations.Schedule.ScheduleEventAttribute"; + public const string LambdaSerializerAttribute = "Amazon.Lambda.Core.LambdaSerializerAttribute"; public const string DefaultLambdaSerializer = "Amazon.Lambda.Serialization.SystemTextJson.DefaultLambdaJsonSerializer"; @@ -84,7 +87,8 @@ public static class TypeFullNames HttpApiAttribute, SQSEventAttribute, ALBApiAttribute, - S3EventAttribute + S3EventAttribute, + ScheduleEventAttribute }; } } \ 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 7e728660a..8b6d8b18a 100644 --- a/Libraries/src/Amazon.Lambda.Annotations.SourceGenerator/Validation/LambdaFunctionValidator.cs +++ b/Libraries/src/Amazon.Lambda.Annotations.SourceGenerator/Validation/LambdaFunctionValidator.cs @@ -4,6 +4,7 @@ using Amazon.Lambda.Annotations.SourceGenerator.Extensions; using Amazon.Lambda.Annotations.SourceGenerator.Models; using Amazon.Lambda.Annotations.SourceGenerator.Models.Attributes; +using Amazon.Lambda.Annotations.Schedule; using Amazon.Lambda.Annotations.SQS; using Microsoft.CodeAnalysis; using System.Collections.Generic; @@ -61,6 +62,7 @@ internal static bool ValidateFunction(GeneratorExecutionContext context, IMethod // Validate Events ValidateApiGatewayEvents(lambdaFunctionModel, methodLocation, diagnostics); ValidateSqsEvents(lambdaFunctionModel, methodLocation, diagnostics); + ValidateScheduleEvents(lambdaFunctionModel, methodLocation, diagnostics); ValidateAlbEvents(lambdaFunctionModel, methodLocation, diagnostics); ValidateS3Events(lambdaFunctionModel, methodLocation, diagnostics); @@ -110,6 +112,16 @@ internal static bool ValidateDependencies(GeneratorExecutionContext context, IMe } } + // Check for references to "Amazon.Lambda.CloudWatchEvents" if the Lambda method is annotated with ScheduleEvent attribute. + if (lambdaMethodSymbol.HasAttribute(context, TypeFullNames.ScheduleEventAttribute)) + { + if (context.Compilation.ReferencedAssemblyNames.FirstOrDefault(x => x.Name == "Amazon.Lambda.CloudWatchEvents") == null) + { + diagnosticReporter.Report(Diagnostic.Create(DiagnosticDescriptors.MissingDependencies, methodLocation, "Amazon.Lambda.CloudWatchEvents")); + return false; + } + } + return true; } @@ -420,6 +432,43 @@ private static void ValidateS3Events(LambdaFunctionModel lambdaFunctionModel, Lo } } + private static void ValidateScheduleEvents(LambdaFunctionModel lambdaFunctionModel, Location methodLocation, List diagnostics) + { + if (!lambdaFunctionModel.LambdaMethod.Events.Contains(EventType.Schedule)) + { + return; + } + + foreach (var att in lambdaFunctionModel.Attributes) + { + if (att.Type.FullName != TypeFullNames.ScheduleEventAttribute) + continue; + + var scheduleEventAttribute = ((AttributeModel)att).Data; + var validationErrors = scheduleEventAttribute.Validate(); + validationErrors.ForEach(errorMessage => diagnostics.Add(Diagnostic.Create(DiagnosticDescriptors.InvalidScheduleEventAttribute, methodLocation, errorMessage))); + } + + // Validate method parameters + var parameters = lambdaFunctionModel.LambdaMethod.Parameters; + if (parameters.Count > 2 || + (parameters.Count >= 1 && parameters[0].Type.FullName != TypeFullNames.ScheduledEvent) || + (parameters.Count == 2 && parameters[1].Type.FullName != TypeFullNames.ILambdaContext)) + { + var errorMessage = $"When using the {nameof(ScheduleEventAttribute)}, the Lambda method can accept at most 2 parameters. " + + $"The first parameter must be of type {TypeFullNames.ScheduledEvent}. " + + $"The second parameter is optional and must be of type {TypeFullNames.ILambdaContext}."; + diagnostics.Add(Diagnostic.Create(DiagnosticDescriptors.InvalidLambdaMethodSignature, methodLocation, errorMessage)); + } + + // Validate return type - must be void or Task + if (!lambdaFunctionModel.LambdaMethod.ReturnsVoid && !lambdaFunctionModel.LambdaMethod.ReturnsVoidTask) + { + var errorMessage = $"When using the {nameof(ScheduleEventAttribute)}, the Lambda method can return either void or {TypeFullNames.Task}"; + diagnostics.Add(Diagnostic.Create(DiagnosticDescriptors.InvalidLambdaMethodSignature, methodLocation, errorMessage)); + } + } + private static bool ReportDiagnostics(DiagnosticReporter diagnosticReporter, List diagnostics) { var isValid = true; diff --git a/Libraries/src/Amazon.Lambda.Annotations.SourceGenerator/Writers/CloudFormationWriter.cs b/Libraries/src/Amazon.Lambda.Annotations.SourceGenerator/Writers/CloudFormationWriter.cs index a72e3241b..a230a78ce 100644 --- a/Libraries/src/Amazon.Lambda.Annotations.SourceGenerator/Writers/CloudFormationWriter.cs +++ b/Libraries/src/Amazon.Lambda.Annotations.SourceGenerator/Writers/CloudFormationWriter.cs @@ -3,6 +3,7 @@ using Amazon.Lambda.Annotations.SourceGenerator.Diagnostics; using Amazon.Lambda.Annotations.SourceGenerator.FileIO; using Amazon.Lambda.Annotations.SourceGenerator.Models; +using Amazon.Lambda.Annotations.Schedule; using Amazon.Lambda.Annotations.SourceGenerator.Models.Attributes; using Amazon.Lambda.Annotations.S3; using Amazon.Lambda.Annotations.SQS; @@ -232,6 +233,10 @@ private void ProcessLambdaFunctionEventAttributes(ILambdaFunctionSerializable la eventName = ProcessS3Attribute(lambdaFunction, s3AttributeModel.Data, currentSyncedEventProperties); currentSyncedEvents.Add(eventName); break; + case AttributeModel scheduleAttributeModel: + eventName = ProcessScheduleAttribute(lambdaFunction, scheduleAttributeModel.Data, currentSyncedEventProperties); + currentSyncedEvents.Add(eventName); + break; } } @@ -608,6 +613,40 @@ private string ProcessSqsAttribute(ILambdaFunctionSerializable lambdaFunction, S return att.ResourceName; } + /// + /// Writes all properties associated with to the serverless template. + /// + private string ProcessScheduleAttribute(ILambdaFunctionSerializable lambdaFunction, ScheduleEventAttribute att, Dictionary> syncedEventProperties) + { + var eventName = att.ResourceName; + var eventPath = $"Resources.{lambdaFunction.ResourceName}.Properties.Events.{eventName}"; + + _templateWriter.SetToken($"{eventPath}.Type", "Schedule"); + + // Schedule expression + SetEventProperty(syncedEventProperties, lambdaFunction.ResourceName, eventName, "Schedule", att.Schedule); + + // Description + if (att.IsDescriptionSet) + { + SetEventProperty(syncedEventProperties, lambdaFunction.ResourceName, eventName, "Description", att.Description); + } + + // Input + if (att.IsInputSet) + { + SetEventProperty(syncedEventProperties, lambdaFunction.ResourceName, eventName, "Input", att.Input); + } + + // Enabled + if (att.IsEnabledSet) + { + SetEventProperty(syncedEventProperties, lambdaFunction.ResourceName, eventName, "Enabled", att.Enabled); + } + + return att.ResourceName; + } + /// /// Writes all properties associated with to the serverless template. /// diff --git a/Libraries/src/Amazon.Lambda.Annotations/README.md b/Libraries/src/Amazon.Lambda.Annotations/README.md index d736e06be..be573af3d 100644 --- a/Libraries/src/Amazon.Lambda.Annotations/README.md +++ b/Libraries/src/Amazon.Lambda.Annotations/README.md @@ -19,6 +19,7 @@ Topics: - [Amazon API Gateway example](#amazon-api-gateway-example) - [Amazon S3 example](#amazon-s3-example) - [SQS Event Example](#sqs-event-example) + - [Schedule Event Example](#schedule-event-example) - [Application Load Balancer (ALB) Example](#application-load-balancer-alb-example) - [Custom Lambda Authorizer Example](#custom-lambda-authorizer-example) - [HTTP API Authorizer](#http-api-authorizer) @@ -1073,6 +1074,25 @@ The `ALBApi` attribute requires an existing ALB listener. Here is a minimal exam Then your Lambda function references `@MyListener` in the `ALBApi` attribute. +## Schedule Event Example +This example shows how to use the `ScheduleEvent` attribute to trigger a Lambda function on a schedule using EventBridge. + +The `ScheduleEvent` attribute contains the following properties: +* **Schedule** (Required) - The schedule expression. Supports `rate()` and `cron()` expressions (e.g. `rate(5 minutes)`, `cron(0 12 * * ? *)`). +* **ResourceName** (Optional) - The CloudFormation resource name for the schedule event. +* **Description** (Optional) - A description for the schedule rule. +* **Input** (Optional) - A JSON string to pass as input to the Lambda function. +* **Enabled** (Optional) - If false, the schedule rule is disabled. Default is true. + +```csharp +[LambdaFunction(ResourceName = "ScheduledHandler", Policies = "AWSLambdaBasicExecutionRole")] +[ScheduleEvent("rate(5 minutes)", ResourceName = "FiveMinuteSchedule", Description = "Runs every 5 minutes")] +public void HandleSchedule(ScheduledEvent evnt, ILambdaContext lambdaContext) +{ + lambdaContext.Logger.Log($"Scheduled event received at {evnt.Time}"); +} +``` + ## Custom Lambda Authorizer Example Lambda Annotations supports defining custom Lambda authorizers using attributes. Custom authorizers let you control access to your API Gateway endpoints by running a Lambda function that validates tokens or request parameters before the target function is invoked. The source generator automatically wires up the authorizer resources and references in the CloudFormation template. @@ -1420,6 +1440,8 @@ parameter to the `LambdaFunction` must be the event object and the event source * Marks a Lambda function as a REST API (API Gateway V1) custom authorizer. The authorizer name is automatically derived from the method name. Other functions reference it via `RestApi.Authorizer` using `nameof()`. Use the `Type` property to choose between `Token` and `Request` authorizer types. * SQSEvent * Sets up event source mapping between the Lambda function and SQS queues. The SQS queue ARN is required to be set on the attribute. If users want to pass a reference to an existing SQS queue resource defined in their CloudFormation template, they can pass the SQS queue resource name prefixed with the '@' symbol. +* ScheduleEvent + * Triggers the Lambda function on a schedule using EventBridge. Supports rate and cron expressions. * ALBApi * Configures the Lambda function to be called from an Application Load Balancer. The listener ARN (or `@ResourceName` template reference), path pattern, and priority are required. The source generator creates standalone CloudFormation resources (TargetGroup, ListenerRule, Lambda Permission) rather than SAM event types. diff --git a/Libraries/src/Amazon.Lambda.Annotations/Schedule/ScheduleEventAttribute.cs b/Libraries/src/Amazon.Lambda.Annotations/Schedule/ScheduleEventAttribute.cs new file mode 100644 index 000000000..0ef7cde51 --- /dev/null +++ b/Libraries/src/Amazon.Lambda.Annotations/Schedule/ScheduleEventAttribute.cs @@ -0,0 +1,96 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text.RegularExpressions; + +namespace Amazon.Lambda.Annotations.Schedule +{ + /// + /// This attribute defines the Schedule event source configuration for a Lambda function. + /// + [AttributeUsage(AttributeTargets.Method, AllowMultiple = false)] + public class ScheduleEventAttribute : Attribute + { + private static readonly Regex _resourceNameRegex = new Regex("^[a-zA-Z0-9]+$"); + + /// + /// The schedule expression. Supports rate and cron expressions. + /// Examples: "rate(5 minutes)", "cron(0 12 * * ? *)" + /// + public string Schedule { get; set; } + + /// + /// The CloudFormation resource name for the schedule event. + /// + public string ResourceName + { + get + { + if (IsResourceNameSet) + { + return resourceName; + } + // Generate a default resource name from the schedule expression + var sanitized = string.Join(string.Empty, Schedule.Where(char.IsLetterOrDigit)); + return sanitized.Length > 0 ? sanitized : "ScheduleEvent"; + } + set => resourceName = value; + } + + private string resourceName { get; set; } = null; + internal bool IsResourceNameSet => resourceName != null; + + /// + /// A description for the schedule rule. + /// + public string Description { get; set; } = null; + internal bool IsDescriptionSet => Description != null; + + /// + /// A JSON string to pass as input to the Lambda function. + /// + public string Input { get; set; } = null; + internal bool IsInputSet => Input != null; + + /// + /// If set to false, the event source mapping will be disabled. Default value is true. + /// + public bool Enabled + { + get => enabled.GetValueOrDefault(); + set => enabled = value; + } + private bool? enabled { get; set; } + internal bool IsEnabledSet => enabled.HasValue; + + /// + /// Creates an instance of the class. + /// + /// property + public ScheduleEventAttribute(string schedule) + { + Schedule = schedule; + } + + internal List Validate() + { + var validationErrors = new List(); + + if (string.IsNullOrEmpty(Schedule)) + { + validationErrors.Add($"{nameof(ScheduleEventAttribute.Schedule)} must not be null or empty"); + } + else if (!Schedule.StartsWith("rate(") && !Schedule.StartsWith("cron(")) + { + validationErrors.Add($"{nameof(ScheduleEventAttribute.Schedule)} = {Schedule}. It must start with 'rate(' or 'cron('"); + } + + if (IsResourceNameSet && !_resourceNameRegex.IsMatch(ResourceName)) + { + validationErrors.Add($"{nameof(ScheduleEventAttribute.ResourceName)} = {ResourceName}. It must only contain alphanumeric characters and must not be an empty string"); + } + + return validationErrors; + } + } +} diff --git a/Libraries/test/Amazon.Lambda.Annotations.SourceGenerators.Tests/Amazon.Lambda.Annotations.SourceGenerators.Tests.csproj b/Libraries/test/Amazon.Lambda.Annotations.SourceGenerators.Tests/Amazon.Lambda.Annotations.SourceGenerators.Tests.csproj index 56da7d597..d3fcd44c4 100644 --- a/Libraries/test/Amazon.Lambda.Annotations.SourceGenerators.Tests/Amazon.Lambda.Annotations.SourceGenerators.Tests.csproj +++ b/Libraries/test/Amazon.Lambda.Annotations.SourceGenerators.Tests/Amazon.Lambda.Annotations.SourceGenerators.Tests.csproj @@ -208,6 +208,7 @@ + + diff --git a/Libraries/test/TestServerlessApp/ScheduleEventExamples/ValidScheduleEvents.cs.txt b/Libraries/test/TestServerlessApp/ScheduleEventExamples/ValidScheduleEvents.cs.txt new file mode 100644 index 000000000..179417209 --- /dev/null +++ b/Libraries/test/TestServerlessApp/ScheduleEventExamples/ValidScheduleEvents.cs.txt @@ -0,0 +1,28 @@ +using Amazon.Lambda.Annotations; +using Amazon.Lambda.Annotations.Schedule; +using Amazon.Lambda.CloudWatchEvents.ScheduledEvents; +using System; +using System.Threading.Tasks; + +namespace TestServerlessApp.ScheduleEventExamples +{ + // This file represents valid usage of the ScheduleEventAttribute. This is added as .txt file since we do not want to deploy these functions during our integration tests. + // This file is only sent as input to the source generator unit tests. + + public class ValidScheduleEvents + { + [LambdaFunction(PackageType = LambdaPackageType.Image)] + [ScheduleEvent("rate(5 minutes)", Description = "Runs every 5 minutes", Input = "{ \"key\": \"value\" }")] + public void ProcessScheduledEvent(ScheduledEvent evnt) + { + Console.WriteLine($"Event processed: {evnt}"); + } + + [LambdaFunction(PackageType = LambdaPackageType.Image)] + [ScheduleEvent("cron(0 12 * * ? *)", ResourceName = "DailyNoonSchedule")] + public async Task ProcessScheduledEventAsync(ScheduledEvent evnt) + { + await Console.Out.WriteLineAsync($"Event processed: {evnt}"); + } + } +} diff --git a/Libraries/test/TestServerlessApp/ScheduledProcessing.cs b/Libraries/test/TestServerlessApp/ScheduledProcessing.cs new file mode 100644 index 000000000..9cd5d43d2 --- /dev/null +++ b/Libraries/test/TestServerlessApp/ScheduledProcessing.cs @@ -0,0 +1,17 @@ +using Amazon.Lambda.Annotations; +using Amazon.Lambda.Annotations.Schedule; +using Amazon.Lambda.CloudWatchEvents.ScheduledEvents; +using Amazon.Lambda.Core; + +namespace TestServerlessApp +{ + public class ScheduledProcessing + { + [LambdaFunction(ResourceName = "ScheduledHandler", Policies = "AWSLambdaBasicExecutionRole")] + [ScheduleEvent("rate(5 minutes)", ResourceName = "FiveMinuteSchedule", Description = "Runs every 5 minutes")] + public void HandleSchedule(ScheduledEvent evnt, ILambdaContext lambdaContext) + { + lambdaContext.Logger.Log($"Scheduled event received at {evnt.Time}"); + } + } +} diff --git a/Libraries/test/TestServerlessApp/TestServerlessApp.csproj b/Libraries/test/TestServerlessApp/TestServerlessApp.csproj index 83d7cf89e..b7ef315c1 100644 --- a/Libraries/test/TestServerlessApp/TestServerlessApp.csproj +++ b/Libraries/test/TestServerlessApp/TestServerlessApp.csproj @@ -27,6 +27,7 @@ +