Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions .autover/changes/durable-execution-annotations-integration.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
{
"Projects": [
{
"Name": "Amazon.Lambda.Annotations",
"Type": "Minor",
"ChangelogMessages": [
"Add [DurableExecution] attribute and source generator support for durable execution functions. A method annotated with [LambdaFunction] and [DurableExecution] generates a handler wrapper that delegates to Amazon.Lambda.DurableExecution.DurableFunction.WrapAsync, and emits a DurableConfig block plus the lambda:CheckpointDurableExecution / lambda:GetDurableExecutionState IAM permissions in the generated CloudFormation/SAM template. Supports both the executable and class-library programming models on the managed runtime. Validates Zip packaging, the (TInput, IDurableContext) -> Task/Task<TOutput> signature, and the RetentionPeriodInDays / ExecutionTimeout bounds (AWSLambda0141-AWSLambda0144). Preview."
]
}
]
}
16 changes: 9 additions & 7 deletions Libraries/Amazon.Lambda.Annotations.slnf
Original file line number Diff line number Diff line change
Expand Up @@ -5,21 +5,23 @@
"src\\Amazon.Lambda.APIGatewayEvents\\Amazon.Lambda.APIGatewayEvents.csproj",
"src\\Amazon.Lambda.Annotations.SourceGenerator\\Amazon.Lambda.Annotations.SourceGenerator.csproj",
"src\\Amazon.Lambda.Annotations\\Amazon.Lambda.Annotations.csproj",
"src\\Amazon.Lambda.ApplicationLoadBalancerEvents\\Amazon.Lambda.ApplicationLoadBalancerEvents.csproj",
"src\\Amazon.Lambda.Core\\Amazon.Lambda.Core.csproj",
"src\\Amazon.Lambda.DurableExecution\\Amazon.Lambda.DurableExecution.csproj",
"src\\Amazon.Lambda.RuntimeSupport\\Amazon.Lambda.RuntimeSupport.csproj",
"src\\Amazon.Lambda.SQSEvents\\Amazon.Lambda.SQSEvents.csproj",
"src\\Amazon.Lambda.Serialization.SystemTextJson\\Amazon.Lambda.Serialization.SystemTextJson.csproj",
"test\\Amazon.Lambda.Annotations.SourceGenerators.Tests\\Amazon.Lambda.Annotations.SourceGenerators.Tests.csproj",
"test\\TestExecutableServerlessApp\\TestExecutableServerlessApp.csproj",
"test\\Amazon.Lambda.DurableExecution.IntegrationTests\\TestFunctions\\AnnotationsClassLibraryFunction\\AnnotationsClassLibraryFunction.csproj",
"test\\IntegrationTests.Helpers\\IntegrationTests.Helpers.csproj",
"test\\TestCustomAuthorizerApp\\TestCustomAuthorizerApp.csproj",
"test\\TestCustomAuthorizerApp.IntegrationTests\\TestCustomAuthorizerApp.IntegrationTests.csproj",
"test\\TestCustomAuthorizerApp\\TestCustomAuthorizerApp.csproj",
"test\\TestExecutableServerlessApp\\TestExecutableServerlessApp.csproj",
"test\\TestServerlessApp.ALB.IntegrationTests\\TestServerlessApp.ALB.IntegrationTests.csproj",
"test\\TestServerlessApp.ALB\\TestServerlessApp.ALB.csproj",
"test\\TestServerlessApp.IntegrationTests\\TestServerlessApp.IntegrationTests.csproj",
"test\\TestServerlessApp.NET8\\TestServerlessApp.NET8.csproj",
"src\\Amazon.Lambda.ApplicationLoadBalancerEvents\\Amazon.Lambda.ApplicationLoadBalancerEvents.csproj",
"test\\TestServerlessApp\\TestServerlessApp.csproj",
"test\\TestServerlessApp.ALB\\TestServerlessApp.ALB.csproj",
"test\\TestServerlessApp.ALB.IntegrationTests\\TestServerlessApp.ALB.IntegrationTests.csproj"
"test\\TestServerlessApp\\TestServerlessApp.csproj"
]
}
}
}
15 changes: 15 additions & 0 deletions Libraries/Libraries.sln
Original file line number Diff line number Diff line change
Expand Up @@ -163,6 +163,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Amazon.Lambda.DurableExecut
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Amazon.Lambda.DurableExecution.AotPublishTest", "test\Amazon.Lambda.DurableExecution.AotPublishTest\Amazon.Lambda.DurableExecution.AotPublishTest.csproj", "{16B1B1CC-3AFC-4DC7-8DB6-D14AE12924A2}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AnnotationsClassLibraryFunction", "test\Amazon.Lambda.DurableExecution.IntegrationTests\TestFunctions\AnnotationsClassLibraryFunction\AnnotationsClassLibraryFunction.csproj", "{D55E2D57-8374-4573-999B-6E64E109C25F}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Expand Down Expand Up @@ -1025,6 +1027,18 @@ Global
{16B1B1CC-3AFC-4DC7-8DB6-D14AE12924A2}.Release|x64.Build.0 = Release|Any CPU
{16B1B1CC-3AFC-4DC7-8DB6-D14AE12924A2}.Release|x86.ActiveCfg = Release|Any CPU
{16B1B1CC-3AFC-4DC7-8DB6-D14AE12924A2}.Release|x86.Build.0 = Release|Any CPU
{D55E2D57-8374-4573-999B-6E64E109C25F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{D55E2D57-8374-4573-999B-6E64E109C25F}.Debug|Any CPU.Build.0 = Debug|Any CPU
{D55E2D57-8374-4573-999B-6E64E109C25F}.Debug|x64.ActiveCfg = Debug|Any CPU
{D55E2D57-8374-4573-999B-6E64E109C25F}.Debug|x64.Build.0 = Debug|Any CPU
{D55E2D57-8374-4573-999B-6E64E109C25F}.Debug|x86.ActiveCfg = Debug|Any CPU
{D55E2D57-8374-4573-999B-6E64E109C25F}.Debug|x86.Build.0 = Debug|Any CPU
{D55E2D57-8374-4573-999B-6E64E109C25F}.Release|Any CPU.ActiveCfg = Release|Any CPU
{D55E2D57-8374-4573-999B-6E64E109C25F}.Release|Any CPU.Build.0 = Release|Any CPU
{D55E2D57-8374-4573-999B-6E64E109C25F}.Release|x64.ActiveCfg = Release|Any CPU
{D55E2D57-8374-4573-999B-6E64E109C25F}.Release|x64.Build.0 = Release|Any CPU
{D55E2D57-8374-4573-999B-6E64E109C25F}.Release|x86.ActiveCfg = Release|Any CPU
{D55E2D57-8374-4573-999B-6E64E109C25F}.Release|x86.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
Expand Down Expand Up @@ -1105,6 +1119,7 @@ Global
{57150BA6-3826-431F-8F58-B1D11FAFC5D4} = {1DE4EE60-45BA-4EF7-BE00-B9EB861E4C69}
{CA132CAB-FF4F-4312-B3A3-66DE9D360F27} = {1DE4EE60-45BA-4EF7-BE00-B9EB861E4C69}
{16B1B1CC-3AFC-4DC7-8DB6-D14AE12924A2} = {1DE4EE60-45BA-4EF7-BE00-B9EB861E4C69}
{D55E2D57-8374-4573-999B-6E64E109C25F} = {1DE4EE60-45BA-4EF7-BE00-B9EB861E4C69}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {503678A4-B8D1-4486-8915-405A3E9CF0EB}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,10 @@
<Generator>TextTemplatingFilePreprocessor</Generator>
<LastGenOutput>AuthorizerInvoke.cs</LastGenOutput>
</None>
<None Update="Templates\DurableExecutionInvoke.tt">
<Generator>TextTemplatingFilePreprocessor</Generator>
<LastGenOutput>DurableExecutionInvoke.cs</LastGenOutput>
</None>
</ItemGroup>

<ItemGroup>
Expand Down Expand Up @@ -151,6 +155,11 @@
<AutoGen>True</AutoGen>
<DependentUpon>AuthorizerInvoke.tt</DependentUpon>
</Compile>
<Compile Update="Templates\DurableExecutionInvoke.cs">
<DesignTime>True</DesignTime>
<AutoGen>True</AutoGen>
<DependentUpon>DurableExecutionInvoke.tt</DependentUpon>
</Compile>
</ItemGroup>

<ItemGroup>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,3 +24,6 @@ AWSLambda0136 | AWSLambdaCSharpGenerator | Error | Invalid S3EventAttribute
AWSLambda0137 | AWSLambdaCSharpGenerator | Error | Invalid DynamoDBEventAttribute
AWSLambda0138 | AWSLambdaCSharpGenerator | Error | Invalid SNSEventAttribute
AWSLambda0139 | AWSLambdaCSharpGenerator | Error | Invalid ScheduleEventAttribute
AWSLambda0142 | AWSLambdaCSharpGenerator | Error | Invalid DurableExecution method signature
AWSLambda0143 | AWSLambdaCSharpGenerator | Info | DurableExecution function with explicit Role needs checkpoint permissions
AWSLambda0144 | AWSLambdaCSharpGenerator | Error | Invalid DurableExecutionAttribute
Original file line number Diff line number Diff line change
Expand Up @@ -302,5 +302,26 @@ public static class DiagnosticDescriptors
category: "AWSLambdaCSharpGenerator",
DiagnosticSeverity.Error,
isEnabledByDefault: true);

public static readonly DiagnosticDescriptor DurableExecutionInvalidSignature = new DiagnosticDescriptor(id: "AWSLambda0142",
title: "Invalid DurableExecution method signature",
messageFormat: "A method annotated with [DurableExecution] must have the signature (TInput, Amazon.Lambda.DurableExecution.IDurableContext) returning Task or Task<TOutput>: {0}",
category: "AWSLambdaCSharpGenerator",
DiagnosticSeverity.Error,
isEnabledByDefault: true);

public static readonly DiagnosticDescriptor DurableExecutionExplicitRoleNeedsCheckpointPolicy = new DiagnosticDescriptor(id: "AWSLambda0143",
title: "DurableExecution function with explicit Role needs checkpoint permissions",
messageFormat: "The [DurableExecution] function uses an explicit Role, so the generator will not add the durable checkpoint policy. Attach the 'lambda:CheckpointDurableExecution' and 'lambda:GetDurableExecutionState' actions to the role manually.",
category: "AWSLambdaCSharpGenerator",
DiagnosticSeverity.Info,
isEnabledByDefault: true);

public static readonly DiagnosticDescriptor InvalidDurableExecutionAttribute = new DiagnosticDescriptor(id: "AWSLambda0144",
title: "Invalid DurableExecutionAttribute",
messageFormat: "Invalid DurableExecutionAttribute encountered: {0}",
category: "AWSLambdaCSharpGenerator",
DiagnosticSeverity.Error,
isEnabledByDefault: true);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -143,6 +143,15 @@ public static AttributeModel Build(AttributeData att, GeneratorExecutionContext
Type = TypeModelBuilder.Build(att.AttributeClass, context)
};
}
else if (att.AttributeClass.Equals(context.Compilation.GetTypeByMetadataName(TypeFullNames.DurableExecutionAttribute), SymbolEqualityComparer.Default))
{
var data = DurableExecutionAttributeBuilder.Build(att);
model = new AttributeModel<DurableExecutionAttribute>
{
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);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0

using Amazon.Lambda.Annotations;
using Microsoft.CodeAnalysis;

namespace Amazon.Lambda.Annotations.SourceGenerator.Models.Attributes
{
/// <summary>
/// Builder for <see cref="DurableExecutionAttribute"/>. Reads named arguments from the
/// <see cref="AttributeData"/>; assigning each property also sets its corresponding
/// <c>IsXxxSet</c> flag so unset values can be omitted from the generated template.
/// </summary>
public class DurableExecutionAttributeBuilder
{
public static DurableExecutionAttribute Build(AttributeData att)
{
var data = new DurableExecutionAttribute();

foreach (var pair in att.NamedArguments)
{
if (pair.Key == nameof(data.RetentionPeriodInDays) && pair.Value.Value is int retentionPeriodInDays)
{
data.RetentionPeriodInDays = retentionPeriodInDays;
}
else if (pair.Key == nameof(data.ExecutionTimeout) && pair.Value.Value is int executionTimeout)
{
data.ExecutionTimeout = executionTimeout;
}
}

return data;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ public enum EventType
DynamoDB,
Schedule,
Authorizer,
ALB
ALB,
DurableExecution
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,10 @@ public static HashSet<EventType> Build(IMethodSymbol lambdaMethodSymbol,
{
events.Add(EventType.Schedule);
}
else if (attribute.AttributeClass.ToDisplayString() == TypeFullNames.DurableExecutionAttribute)
{
events.Add(EventType.DurableExecution);
}
else if (attribute.AttributeClass.ToDisplayString() == TypeFullNames.HttpApiAuthorizerAttribute
|| attribute.AttributeClass.ToDisplayString() == TypeFullNames.RestApiAuthorizerAttribute)
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,11 @@ private static IList<string> BuildUsings(LambdaMethodModel lambdaMethodModel,
namespaces.Add("Amazon.Lambda.Annotations.APIGateway");
}

if (lambdaMethodSymbol.HasAttribute(context, TypeFullNames.DurableExecutionAttribute))
{
namespaces.Add("Amazon.Lambda.DurableExecution");
}

return namespaces;
}

Expand All @@ -71,6 +76,15 @@ private static TypeModel BuildResponseType(IMethodSymbol lambdaMethodSymbol,
{
var task = context.Compilation.GetTypeByMetadataName(TypeFullNames.Task1);

// Durable functions always produce Task<DurableExecutionInvocationOutput>; the generated
// wrapper delegates to DurableFunction.WrapAsync, which returns that envelope.
if (lambdaMethodSymbol.HasAttribute(context, TypeFullNames.DurableExecutionAttribute))
{
var outputType = context.Compilation.GetTypeByMetadataName(TypeFullNames.DurableExecutionInvocationOutput);
var genericTask = task.Construct(outputType);
return TypeModelBuilder.Build(genericTask, context);
}

if (lambdaMethodModel.ReturnsIHttpResults)
{
var typeStream = context.Compilation.GetTypeByMetadataName(TypeFullNames.Stream);
Expand Down Expand Up @@ -217,7 +231,22 @@ private static IList<ParameterModel> BuildParameters(IMethodSymbol lambdaMethodS
Documentation = "The ILambdaContext that provides methods for logging and describing the Lambda environment."
};

if (lambdaMethodSymbol.HasAttribute(context, TypeFullNames.HttpApiAuthorizerAttribute))
// Durable functions receive the service envelope (DurableExecutionInvocationInput); the
// generated wrapper passes it straight to DurableFunction.WrapAsync along with the context.
if (lambdaMethodSymbol.HasAttribute(context, TypeFullNames.DurableExecutionAttribute))
{
var symbol = context.Compilation.GetTypeByMetadataName(TypeFullNames.DurableExecutionInvocationInput);
var type = TypeModelBuilder.Build(symbol, context);
var requestParameter = new ParameterModel
{
Name = "__request__",
Type = type,
Documentation = "The durable execution service envelope that will be processed by the Lambda function handler."
};
parameters.Add(requestParameter);
parameters.Add(contextParameter);
}
else if (lambdaMethodSymbol.HasAttribute(context, TypeFullNames.HttpApiAuthorizerAttribute))
{
// For HTTP API authorizer functions, the generated handler accepts the authorizer request type
var authorizerAttribute = lambdaMethodSymbol.GetAttributeData(context, TypeFullNames.HttpApiAuthorizerAttribute);
Expand Down
Loading