Skip to content

Commit 39eda7f

Browse files
fromx support for authorizers (#2304)
1 parent 5495eeb commit 39eda7f

48 files changed

Lines changed: 4205 additions & 139 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

Libraries/src/Amazon.Lambda.Annotations.SourceGenerator/Amazon.Lambda.Annotations.SourceGenerator.csproj

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -103,6 +103,14 @@
103103
<Generator>TextTemplatingFilePreprocessor</Generator>
104104
<LastGenOutput>ExecutableAssembly.cs</LastGenOutput>
105105
</None>
106+
<None Update="Templates\AuthorizerSetupParameters.tt">
107+
<Generator>TextTemplatingFilePreprocessor</Generator>
108+
<LastGenOutput>AuthorizerSetupParameters.cs</LastGenOutput>
109+
</None>
110+
<None Update="Templates\AuthorizerInvoke.tt">
111+
<Generator>TextTemplatingFilePreprocessor</Generator>
112+
<LastGenOutput>AuthorizerInvoke.cs</LastGenOutput>
113+
</None>
106114
</ItemGroup>
107115

108116
<ItemGroup>
@@ -136,6 +144,16 @@
136144
<AutoGen>True</AutoGen>
137145
<DependentUpon>ExecutableAssembly.tt</DependentUpon>
138146
</Compile>
147+
<Compile Update="Templates\AuthorizerSetupParameters.cs">
148+
<DesignTime>True</DesignTime>
149+
<AutoGen>True</AutoGen>
150+
<DependentUpon>AuthorizerSetupParameters.tt</DependentUpon>
151+
</Compile>
152+
<Compile Update="Templates\AuthorizerInvoke.cs">
153+
<DesignTime>True</DesignTime>
154+
<AutoGen>True</AutoGen>
155+
<DependentUpon>AuthorizerInvoke.tt</DependentUpon>
156+
</Compile>
139157
</ItemGroup>
140158

141159
<ItemGroup>

Libraries/src/Amazon.Lambda.Annotations.SourceGenerator/Diagnostics/AnalyzerReleases.Unshipped.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,3 +14,5 @@ AWSLambda0125 | AWSLambdaCSharpGenerator | Error | Duplicate Authorizer Name
1414
AWSLambda0127 | AWSLambdaCSharpGenerator | Error | Invalid Result TTL
1515
AWSLambda0128 | AWSLambdaCSharpGenerator | Error | Authorizer Payload Version Mismatch
1616
AWSLambda0129 | AWSLambdaCSharpGenerator | Error | Missing LambdaFunction Attribute
17+
AWSLambda0130 | AWSLambdaCSharpGenerator | Error | Invalid return type IAuthorizerResult
18+
AWSLambda0131 | AWSLambdaCSharpGenerator | Error | FromBody not supported on Authorizer functions

Libraries/src/Amazon.Lambda.Annotations.SourceGenerator/Diagnostics/DiagnosticDescriptors.cs

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -216,7 +216,7 @@ public static class DiagnosticDescriptors
216216
title: "Authorizer Payload Version Mismatch",
217217
messageFormat: "The authorizer '{0}' uses AuthorizerPayloadFormatVersion {1} but the endpoint uses HttpApiVersion {2}. This may cause unexpected behavior.",
218218
category: "AWSLambdaCSharpGenerator",
219-
DiagnosticSeverity.Warning,
219+
DiagnosticSeverity.Error,
220220
isEnabledByDefault: true);
221221

222222
public static readonly DiagnosticDescriptor MissingLambdaFunctionAttribute = new DiagnosticDescriptor(
@@ -226,5 +226,21 @@ public static class DiagnosticDescriptors
226226
category: "AWSLambdaCSharpGenerator",
227227
DiagnosticSeverity.Error,
228228
isEnabledByDefault: true);
229+
230+
public static readonly DiagnosticDescriptor AuthorizerResultOnNonAuthorizerFunction = new DiagnosticDescriptor(
231+
id: "AWSLambda0130",
232+
title: $"Invalid return type IAuthorizerResult",
233+
messageFormat: "IAuthorizerResult is not a valid return type for LambdaFunctions without HttpApiAuthorizer or RestApiAuthorizer attributes",
234+
category: "AWSLambdaCSharpGenerator",
235+
DiagnosticSeverity.Error,
236+
isEnabledByDefault: true);
237+
238+
public static readonly DiagnosticDescriptor FromBodyNotSupportedOnAuthorizer = new DiagnosticDescriptor(
239+
id: "AWSLambda0131",
240+
title: "FromBody not supported on Authorizer functions",
241+
messageFormat: "[FromBody] is not supported on authorizer functions. Authorizer functions only support [FromHeader], [FromQuery], and [FromRoute] parameter attributes.",
242+
category: "AWSLambdaCSharpGenerator",
243+
DiagnosticSeverity.Error,
244+
isEnabledByDefault: true);
229245
}
230246
}

Libraries/src/Amazon.Lambda.Annotations.SourceGenerator/Extensions/ParameterListExtension.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ public static bool HasConvertibleParameter(this IList<ParameterModel> parameters
1212
return parameters.Any(p =>
1313
{
1414
// All request types are forwarded to lambda method if specified, there is no parameter conversion required.
15-
if (TypeFullNames.Requests.Contains(p.Type.FullName))
15+
if (TypeFullNames.ApiGatewayRequests.Contains(p.Type.FullName))
1616
{
1717
return false;
1818
}

Libraries/src/Amazon.Lambda.Annotations.SourceGenerator/Models/Attributes/AttributeModelBuilder.cs

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,24 @@ public static AttributeModel Build(AttributeData att, GeneratorExecutionContext
9090
Type = TypeModelBuilder.Build(att.AttributeClass, context)
9191
};
9292
}
93+
else if (att.AttributeClass.Equals(context.Compilation.GetTypeByMetadataName(TypeFullNames.HttpApiAuthorizerAttribute), SymbolEqualityComparer.Default))
94+
{
95+
var data = HttpApiAuthorizerAttributeBuilder.Build(att);
96+
model = new AttributeModel<HttpApiAuthorizerAttribute>
97+
{
98+
Data = data,
99+
Type = TypeModelBuilder.Build(att.AttributeClass, context)
100+
};
101+
}
102+
else if (att.AttributeClass.Equals(context.Compilation.GetTypeByMetadataName(TypeFullNames.RestApiAuthorizerAttribute), SymbolEqualityComparer.Default))
103+
{
104+
var data = RestApiAuthorizerAttributeBuilder.Build(att);
105+
model = new AttributeModel<RestApiAuthorizerAttribute>
106+
{
107+
Data = data,
108+
Type = TypeModelBuilder.Build(att.AttributeClass, context)
109+
};
110+
}
93111
else
94112
{
95113
model = new AttributeModel

Libraries/src/Amazon.Lambda.Annotations.SourceGenerator/Models/EventType.cs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ public enum EventType
1010
S3,
1111
SQS,
1212
DynamoDB,
13-
Schedule
13+
Schedule,
14+
Authorizer
1415
}
1516
}

Libraries/src/Amazon.Lambda.Annotations.SourceGenerator/Models/EventTypeBuilder.cs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,11 @@ public static HashSet<EventType> Build(IMethodSymbol lambdaMethodSymbol,
2626
{
2727
events.Add(EventType.SQS);
2828
}
29+
else if (attribute.AttributeClass.ToDisplayString() == TypeFullNames.HttpApiAuthorizerAttribute
30+
|| attribute.AttributeClass.ToDisplayString() == TypeFullNames.RestApiAuthorizerAttribute)
31+
{
32+
events.Add(EventType.Authorizer);
33+
}
2934
}
3035

3136
return events;

Libraries/src/Amazon.Lambda.Annotations.SourceGenerator/Models/GeneratedMethodModelBuilder.cs

Lines changed: 77 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,7 @@ private static IList<string> BuildUsings(LambdaMethodModel lambdaMethodModel,
5555

5656
namespaces.Add("Amazon.Lambda.Core");
5757

58-
if(lambdaMethodModel.ReturnsIHttpResults)
58+
if(lambdaMethodModel.ReturnsIHttpResults || lambdaMethodModel.ReturnsIAuthorizerResult)
5959
{
6060
namespaces.Add("Amazon.Lambda.Annotations.APIGateway");
6161
}
@@ -79,6 +79,26 @@ private static TypeModel BuildResponseType(IMethodSymbol lambdaMethodSymbol,
7979
return TypeModelBuilder.Build(typeStream, context);
8080
}
8181

82+
// For authorizer functions returning IAuthorizerResult, the generated handler returns Stream
83+
// (the IAuthorizerResult.Serialize method produces the JSON stream)
84+
if (lambdaMethodModel.ReturnsIAuthorizerResult)
85+
{
86+
var typeStream = context.Compilation.GetTypeByMetadataName(TypeFullNames.Stream);
87+
if (lambdaMethodModel.ReturnsGenericTask)
88+
{
89+
var genericTask = task.Construct(typeStream);
90+
return TypeModelBuilder.Build(genericTask, context);
91+
}
92+
return TypeModelBuilder.Build(typeStream, context);
93+
}
94+
95+
// For authorizer functions that return raw API Gateway types (backwards compatibility),
96+
// pass through the return type as-is
97+
if (lambdaMethodSymbol.HasAttribute(context, TypeFullNames.HttpApiAuthorizerAttribute) ||
98+
lambdaMethodSymbol.HasAttribute(context, TypeFullNames.RestApiAuthorizerAttribute))
99+
{
100+
return lambdaMethodModel.ReturnType;
101+
}
82102

83103
if (lambdaMethodSymbol.HasAttribute(context, TypeFullNames.RestApiAttribute))
84104
{
@@ -143,6 +163,20 @@ private static HttpApiVersion GetHttpApiVersion(IMethodSymbol lambdaMethodSymbol
143163
return (HttpApiVersion)versionArgument.Value;
144164
}
145165

166+
private static AuthorizerPayloadFormatVersion GetAuthorizerPayloadFormatVersion(AttributeData authorizerAttribute)
167+
{
168+
var versionArg = authorizerAttribute.NamedArguments
169+
.FirstOrDefault(arg => arg.Key == "AuthorizerPayloadFormatVersion").Value;
170+
171+
if (versionArg.Type == null || versionArg.Value == null)
172+
{
173+
// Default is V2
174+
return AuthorizerPayloadFormatVersion.V2;
175+
}
176+
177+
return (AuthorizerPayloadFormatVersion)(int)versionArg.Value;
178+
}
179+
146180
private static IList<ParameterModel> BuildParameters(IMethodSymbol lambdaMethodSymbol,
147181
LambdaMethodModel lambdaMethodModel, GeneratorExecutionContext context)
148182
{
@@ -158,7 +192,48 @@ private static IList<ParameterModel> BuildParameters(IMethodSymbol lambdaMethodS
158192
Documentation = "The ILambdaContext that provides methods for logging and describing the Lambda environment."
159193
};
160194

161-
if (lambdaMethodSymbol.HasAttribute(context, TypeFullNames.RestApiAttribute))
195+
if (lambdaMethodSymbol.HasAttribute(context, TypeFullNames.HttpApiAuthorizerAttribute))
196+
{
197+
// For HTTP API authorizer functions, the generated handler accepts the authorizer request type
198+
var authorizerAttribute = lambdaMethodSymbol.GetAttributeData(context, TypeFullNames.HttpApiAuthorizerAttribute);
199+
var payloadVersion = GetAuthorizerPayloadFormatVersion(authorizerAttribute);
200+
201+
string requestTypeName;
202+
if (payloadVersion == AuthorizerPayloadFormatVersion.V2)
203+
{
204+
requestTypeName = TypeFullNames.APIGatewayCustomAuthorizerV2Request;
205+
}
206+
else
207+
{
208+
requestTypeName = TypeFullNames.APIGatewayCustomAuthorizerRequest;
209+
}
210+
211+
var symbol = context.Compilation.GetTypeByMetadataName(requestTypeName);
212+
var type = TypeModelBuilder.Build(symbol, context);
213+
var requestParameter = new ParameterModel
214+
{
215+
Name = "__request__",
216+
Type = type,
217+
Documentation = "The API Gateway authorizer request object that will be processed by the Lambda function handler."
218+
};
219+
parameters.Add(requestParameter);
220+
parameters.Add(contextParameter);
221+
}
222+
else if (lambdaMethodSymbol.HasAttribute(context, TypeFullNames.RestApiAuthorizerAttribute))
223+
{
224+
// For REST API authorizer functions, always use APIGatewayCustomAuthorizerRequest
225+
var symbol = context.Compilation.GetTypeByMetadataName(TypeFullNames.APIGatewayCustomAuthorizerRequest);
226+
var type = TypeModelBuilder.Build(symbol, context);
227+
var requestParameter = new ParameterModel
228+
{
229+
Name = "__request__",
230+
Type = type,
231+
Documentation = "The API Gateway authorizer request object that will be processed by the Lambda function handler."
232+
};
233+
parameters.Add(requestParameter);
234+
parameters.Add(contextParameter);
235+
}
236+
else if (lambdaMethodSymbol.HasAttribute(context, TypeFullNames.RestApiAttribute))
162237
{
163238
var symbol = context.Compilation.GetTypeByMetadataName(TypeFullNames.APIGatewayProxyRequest);
164239
var type = TypeModelBuilder.Build(symbol, context);

Libraries/src/Amazon.Lambda.Annotations.SourceGenerator/Models/LambdaMethodModel.cs

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,31 @@ public bool ReturnsIHttpResults
6464
}
6565
}
6666

67+
/// <summary>
68+
/// Returns true if the Lambda function returns either IAuthorizerResult or Task&lt;IAuthorizerResult&gt;
69+
/// </summary>
70+
public bool ReturnsIAuthorizerResult
71+
{
72+
get
73+
{
74+
if (ReturnsVoid)
75+
{
76+
return false;
77+
}
78+
79+
if (ReturnType.FullName == TypeFullNames.IAuthorizerResult)
80+
{
81+
return true;
82+
}
83+
if (ReturnsGenericTask && ReturnType.TypeArguments.Count == 1 && ReturnType.TypeArguments[0].FullName == TypeFullNames.IAuthorizerResult)
84+
{
85+
return true;
86+
}
87+
88+
return false;
89+
}
90+
}
91+
6792
/// <summary>
6893
/// Returns true if the Lambda function returns either void, Task, SQSBatchResponse or Task<SQSBatchResponse>
6994
/// </summary>

Libraries/src/Amazon.Lambda.Annotations.SourceGenerator/Templates/APIGatewaySetupParameters.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@ public virtual string TransformText()
4545
}
4646

4747
// Pass the same request parameter for Request Type that comes from the generated method.
48-
if (TypeFullNames.Requests.Contains(p.Type.FullName))
48+
if (TypeFullNames.ApiGatewayRequests.Contains(p.Type.FullName))
4949
{
5050
return "__request__";
5151
}
@@ -83,7 +83,7 @@ public virtual string TransformText()
8383

8484
foreach (var parameter in _model.LambdaMethod.Parameters)
8585
{
86-
if (parameter.Type.FullName == TypeFullNames.ILambdaContext || TypeFullNames.Requests.Contains(parameter.Type.FullName))
86+
if (parameter.Type.FullName == TypeFullNames.ILambdaContext || TypeFullNames.ApiGatewayRequests.Contains(parameter.Type.FullName))
8787
{
8888
// No action required for ILambdaContext and RequestType, they are passed from the generated method parameter directly to the original method.
8989
}

0 commit comments

Comments
 (0)