Skip to content

Commit f246c92

Browse files
committed
fix: Use the ILambdaSerializer to serialize the response body when returning an IHttpResult
BREAKING CHANGE: Removed the JsonContext property in the HttpResultSerializationOptions class and replaced it with an ILambdaSerializer
1 parent e86fa63 commit f246c92

28 files changed

+349
-300
lines changed

Libraries/src/Amazon.Lambda.Annotations.SourceGenerator/Generator.cs

Lines changed: 2 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -356,7 +356,6 @@ private bool HasSerializerAttribute(GeneratorExecutionContext context, IMethodSy
356356
private LambdaSerializerInfo GetSerializerInfoAttribute(GeneratorExecutionContext context, IMethodSymbol methodModel)
357357
{
358358
var serializerString = DEFAULT_LAMBDA_SERIALIZER;
359-
string serializerJsonContext = null;
360359

361360
ISymbol symbol = null;
362361

@@ -377,24 +376,19 @@ private LambdaSerializerInfo GetSerializerInfoAttribute(GeneratorExecutionContex
377376
// Else return the default serializer.
378377
else
379378
{
380-
return new LambdaSerializerInfo(serializerString, serializerJsonContext);
379+
return new LambdaSerializerInfo(serializerString);
381380
}
382381

383382
var attribute = symbol.GetAttributes().FirstOrDefault(attr => attr.AttributeClass.Name == TypeFullNames.LambdaSerializerAttributeWithoutNamespace);
384383

385384
var serializerValue = attribute.ConstructorArguments.FirstOrDefault(kvp => kvp.Type.Name == nameof(Type)).Value;
386385

387-
if(serializerValue is INamedTypeSymbol typeSymbol && typeSymbol.Name.Contains("SourceGeneratorLambdaJsonSerializer") && typeSymbol.TypeArguments.Length == 1)
388-
{
389-
serializerJsonContext = typeSymbol.TypeArguments[0].ToString();
390-
}
391-
392386
if (serializerValue != null)
393387
{
394388
serializerString = serializerValue.ToString();
395389
}
396390

397-
return new LambdaSerializerInfo(serializerString, serializerJsonContext);
391+
return new LambdaSerializerInfo(serializerString);
398392
}
399393

400394
public void Initialize(GeneratorInitializationContext context)

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ public class LambdaFunctionModel : ILambdaFunctionSerializable
3535
/// Gets or sets fully qualified name of the serializer used for serialization or deserialization.
3636
/// </summary>
3737
public LambdaSerializerInfo SerializerInfo { get; set; } =
38-
new LambdaSerializerInfo("Amazon.Lambda.Serialization.SystemTextJson.DefaultLambdaJsonSerializer", null);
38+
new LambdaSerializerInfo("Amazon.Lambda.Serialization.SystemTextJson.DefaultLambdaJsonSerializer");
3939

4040
/// <summary>
4141
/// Gets or sets if the output is an executable.

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

Lines changed: 1 addition & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -14,20 +14,14 @@ public class LambdaSerializerInfo
1414
/// </summary>
1515
/// <param name="serializerName"></param>
1616
/// <param name="serializerJsonContextName"></param>
17-
public LambdaSerializerInfo(string serializerName, string serializerJsonContextName)
17+
public LambdaSerializerInfo(string serializerName)
1818
{
1919
SerializerName = serializerName;
20-
SerializerJsonContextName = serializerJsonContextName;
2120
}
2221

2322
/// <summary>
2423
/// The full name of the type registered as the ILambdaSerializer.
2524
/// </summary>
2625
public string SerializerName { get; }
27-
28-
/// <summary>
29-
/// The full name of the type used as the generic parameter of the SourceGeneratorLambdaJsonSerializer.
30-
/// </summary>
31-
public string SerializerJsonContextName { get; }
3226
}
3327
}

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

Lines changed: 34 additions & 42 deletions
Large diffs are not rendered by default.

Libraries/src/Amazon.Lambda.Annotations.SourceGenerator/Templates/APIGatewayInvoke.tt

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,8 +17,7 @@
1717
var httpResults = <#= _model.LambdaMethod.ReturnsGenericTask ? "await " : "" #><#= _model.LambdaMethod.ContainingType.Name.ToCamelCase() #>.<#= _model.LambdaMethod.Name #>(<#= _parameterSignature #>);
1818
HttpResultSerializationOptions.ProtocolFormat serializationFormat = <#= restApiAttribute != null ? "HttpResultSerializationOptions.ProtocolFormat.RestApi" : "HttpResultSerializationOptions.ProtocolFormat.HttpApi"#>;
1919
HttpResultSerializationOptions.ProtocolVersion serializationVersion = <#= restApiAttribute != null || httpApiAttribute?.Data.Version == Amazon.Lambda.Annotations.APIGateway.HttpApiVersion.V1 ? "HttpResultSerializationOptions.ProtocolVersion.V1" : "HttpResultSerializationOptions.ProtocolVersion.V2"#>;
20-
System.Text.Json.Serialization.JsonSerializerContext jsonContext = <#= _model.SerializerInfo.SerializerJsonContextName != null ? _model.SerializerInfo.SerializerJsonContextName + ".Default" : "null"#>;
21-
var serializationOptions = new HttpResultSerializationOptions { Format = serializationFormat, Version = serializationVersion, JsonContext = jsonContext };
20+
var serializationOptions = new HttpResultSerializationOptions { Format = serializationFormat, Version = serializationVersion, Serializer = serializer };
2221
var response = httpResults.Serialize(serializationOptions);
2322
<#
2423
}

Libraries/src/Amazon.Lambda.Annotations/APIGateway/HttpResults.cs

Lines changed: 11 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@
33
using System.IO;
44
using System.Linq;
55
using System.Net;
6+
using Amazon.Lambda.Core;
7+
68
#if NET6_0_OR_GREATER
79
using System.Buffers;
810
using System.Text.Json;
@@ -66,16 +68,11 @@ public enum ProtocolVersion {
6668
/// </summary>
6769
public ProtocolVersion Version { get; set; }
6870

69-
#if !NETSTANDARD2_0
7071

7172
/// <summary>
72-
/// The JsonSerializerContext used for serializing response body using .NET's source generator serializer.
73-
/// This is set when the SourceGeneratorLambdaJsonSerializer serializer is registered taking the JsonSerializerContext
74-
/// assigned with it. If SourceGeneratorLambdaJsonSerializer is not used then the reflection based
75-
/// JsonSerializer.Serialize method is used.
73+
/// The JSON serializer used for serializing the response body.
7674
/// </summary>
77-
public JsonSerializerContext JsonContext { get; set; }
78-
#endif
75+
public ILambdaSerializer Serializer { get; set; }
7976
}
8077

8178
/// <summary>
@@ -350,13 +347,7 @@ public static IHttpResult NewResult(HttpStatusCode statusCode, object body = nul
350347

351348

352349
#if !NETSTANDARD2_0
353-
// See comment in class documentation on the rules for serializing. If any changes are made in this method be sure to update
354-
// the comment above.
355-
[System.Diagnostics.CodeAnalysis.UnconditionalSuppressMessage("ReflectionAnalysis", "IL2026",
356-
Justification = "When using this library with the Native AOT the SourceGeneratorLambdaJsonSerializer has to be registered which will provide the information needed for JsonSerializerContext and avoid the reflection based JsonSerializer.Serialize call")]
357-
[System.Diagnostics.CodeAnalysis.UnconditionalSuppressMessage("ReflectionAnalysis", "IL3050",
358-
Justification = "When using this library with the Native AOT the SourceGeneratorLambdaJsonSerializer has to be registered which will provide the information needed for JsonSerializerContext and avoid the reflection based JsonSerializer.Serialize call")]
359-
private static (string body, string contentType, bool base64Encoded) FormatBody(object body, JsonSerializerContext context)
350+
private static (string body, string contentType, bool base64Encoded) FormatBody(object body, ILambdaSerializer serializer)
360351
{
361352
if (body == null)
362353
return new (null, null, false);
@@ -392,16 +383,10 @@ private static (string body, string contentType, bool base64Encoded) FormatBody(
392383
}
393384
else
394385
{
395-
string serializedBody;
396-
if (context != null)
397-
{
398-
serializedBody = JsonSerializer.Serialize(body, body.GetType(), context);
399-
}
400-
else
401-
{
402-
serializedBody = JsonSerializer.Serialize(body);
403-
}
404-
386+
var bodyStream = new MemoryStream();
387+
serializer.Serialize(body, bodyStream);
388+
bodyStream.Position = 0;
389+
var serializedBody = new StreamReader(bodyStream).ReadToEnd();
405390
return new(serializedBody, CONTENT_TYPE_APPLICATION_JSON, false);
406391
}
407392
}
@@ -421,15 +406,14 @@ public Stream Serialize(HttpResultSerializationOptions options)
421406
throw new NotImplementedException();
422407
#else
423408

424-
var (serializedBody, defaultContentType, isBase64Encoded) = FormatBody(_rawBody, options.JsonContext);
409+
var (serializedBody, defaultContentType, isBase64Encoded) = FormatBody(_rawBody, options.Serializer);
425410

426411
// If the user didn't explicit set the content type then default to application/json
427412
if (!string.IsNullOrEmpty(serializedBody) && (_headers == null || !_headers.ContainsKey(HEADER_NAME_CONTENT_TYPE)))
428413
{
429414
AddHeader(HEADER_NAME_CONTENT_TYPE, defaultContentType);
430415
}
431416

432-
var stream = new MemoryStream();
433417
object response;
434418
Type responseType;
435419
if (options.Format == HttpResultSerializationOptions.ProtocolFormat.RestApi ||
@@ -457,9 +441,8 @@ public Stream Serialize(HttpResultSerializationOptions options)
457441

458442
responseType = typeof(APIGatewayV2Response);
459443
}
460-
444+
var stream = new MemoryStream();
461445
JsonSerializer.Serialize(stream, response, responseType, AnnotationsResponseJsonSerializerContext.Default);
462-
463446
stream.Position = 0;
464447
return stream;
465448
#endif

Libraries/test/Amazon.Lambda.Annotations.SourceGenerators.Tests/Amazon.Lambda.Annotations.SourceGenerators.Tests.csproj

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
<PackageReference Include="Moq" Version="4.18.4" />
1212
<PackageReference Include="Newtonsoft.Json" Version="13.0.2" />
1313
<PackageReference Include="System.Memory" Version="4.5.5" />
14+
<PackageReference Include="System.Text.Json" Version="8.0.3" />
1415
<PackageReference Include="xunit.runner.visualstudio" Version="2.4.5">
1516
<PrivateAssets>all</PrivateAssets>
1617
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>

Libraries/test/Amazon.Lambda.Annotations.SourceGenerators.Tests/HtttpResultsStatusCodeUsage.cs renamed to Libraries/test/Amazon.Lambda.Annotations.SourceGenerators.Tests/HttpResultsStatusCodeUsage.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55

66
namespace Amazon.Lambda.Annotations.SourceGenerators.Tests
77
{
8-
public class HtttpResultsStatusCodeUsage
8+
public class HttpResultsStatusCodeUsage
99
{
1010
[Fact]
1111
public void UsageOfIHttpResultStatusCode()

Libraries/test/Amazon.Lambda.Annotations.SourceGenerators.Tests/HttpResultsTest.cs

Lines changed: 51 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,9 @@
66
using Xunit;
77
using System.IO;
88
using System.Linq;
9+
using Amazon.Lambda.Serialization.SystemTextJson;
10+
using Amazon.Lambda.Core;
11+
using Microsoft.VisualStudio.TestPlatform.PlatformAbstractions.Interfaces;
912

1013
namespace Amazon.Lambda.Annotations.SourceGenerators.Tests
1114
{
@@ -357,21 +360,42 @@ public void ServiceUnavailable_WithRetryAfter()
357360
});
358361
}
359362

363+
[Fact]
364+
public void HttpResult_WithCustomSerializer()
365+
{
366+
var result = HttpResults.Ok(new Person { FirstName = "John", LastName = "Doe" });
367+
var response = result.Serialize(new HttpResultSerializationOptions
368+
{
369+
Format = HttpResultSerializationOptions.ProtocolFormat.HttpApi,
370+
Version = HttpResultSerializationOptions.ProtocolVersion.V2,
371+
Serializer = new CustomLambdaSerializer()
372+
});
373+
374+
var jsonDoc = JsonDocument.Parse(response);
375+
Assert.Equal(200, jsonDoc.RootElement.GetProperty("statusCode").GetInt32());
376+
377+
var body = jsonDoc.RootElement.GetProperty("body").GetString();
378+
var person = JsonSerializer.Deserialize<Dictionary<string, string>>(body);
379+
Assert.Equal("John", person["FIRST_NAME"]);
380+
Assert.Equal("Doe", person["LAST_NAME"]);
381+
}
382+
360383

361384
private void ValidateResult(Func<IHttpResult> resultCreator, HttpStatusCode statusCode, string body = null, bool isBase64Encoded = false, IDictionary<string, IList<string>> headers = null)
362385
{
363-
var testScenarios = new List<Tuple<HttpResultSerializationOptions.ProtocolFormat, HttpResultSerializationOptions.ProtocolVersion>>
386+
var lambdaSerializer = new DefaultLambdaJsonSerializer();
387+
var testScenarios = new List<Tuple<HttpResultSerializationOptions.ProtocolFormat, HttpResultSerializationOptions.ProtocolVersion, ILambdaSerializer>>
364388
{
365-
new (HttpResultSerializationOptions.ProtocolFormat.RestApi, HttpResultSerializationOptions.ProtocolVersion.V1),
366-
new (HttpResultSerializationOptions.ProtocolFormat.HttpApi, HttpResultSerializationOptions.ProtocolVersion.V1),
367-
new (HttpResultSerializationOptions.ProtocolFormat.HttpApi, HttpResultSerializationOptions.ProtocolVersion.V2)
389+
new (HttpResultSerializationOptions.ProtocolFormat.RestApi, HttpResultSerializationOptions.ProtocolVersion.V1, lambdaSerializer),
390+
new (HttpResultSerializationOptions.ProtocolFormat.HttpApi, HttpResultSerializationOptions.ProtocolVersion.V1, lambdaSerializer),
391+
new (HttpResultSerializationOptions.ProtocolFormat.HttpApi, HttpResultSerializationOptions.ProtocolVersion.V2, lambdaSerializer)
368392
};
369393

370-
foreach(var (format, version) in testScenarios)
394+
foreach(var (format, version, serializer) in testScenarios)
371395
{
372396
IHttpResult result = resultCreator();
373397

374-
var stream = result.Serialize(new HttpResultSerializationOptions { Format = format, Version = version });
398+
var stream = result.Serialize(new HttpResultSerializationOptions { Format = format, Version = version, Serializer = serializer });
375399
var jsonDoc = JsonDocument.Parse(stream);
376400
if (format == HttpResultSerializationOptions.ProtocolFormat.RestApi || (format == HttpResultSerializationOptions.ProtocolFormat.HttpApi && version == HttpResultSerializationOptions.ProtocolVersion.V1))
377401
{
@@ -456,5 +480,26 @@ public class FakeBody
456480
{
457481
public int Id { get; set; } = 1;
458482
}
483+
484+
public class Person
485+
{
486+
public string FirstName { get; set; }
487+
public string LastName { get; set; }
488+
}
489+
490+
public class CustomLambdaSerializer : DefaultLambdaJsonSerializer
491+
{
492+
public CustomLambdaSerializer()
493+
: base(CreateCustomizer())
494+
{ }
495+
496+
private static Action<JsonSerializerOptions> CreateCustomizer()
497+
{
498+
return (JsonSerializerOptions options) =>
499+
{
500+
options.PropertyNamingPolicy = JsonNamingPolicy.SnakeCaseUpper;
501+
};
502+
}
503+
}
459504
}
460505
}

Libraries/test/Amazon.Lambda.Annotations.SourceGenerators.Tests/Snapshots/CustomizeResponseExamples_NotFoundResponseWithHeaderV1Async_Generated.g.cs

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -73,8 +73,7 @@ public CustomizeResponseExamples_NotFoundResponseWithHeaderV1Async_Generated()
7373
var httpResults = await customizeResponseExamples.NotFoundResponseWithHeaderV1Async(x, __context__);
7474
HttpResultSerializationOptions.ProtocolFormat serializationFormat = HttpResultSerializationOptions.ProtocolFormat.HttpApi;
7575
HttpResultSerializationOptions.ProtocolVersion serializationVersion = HttpResultSerializationOptions.ProtocolVersion.V1;
76-
System.Text.Json.Serialization.JsonSerializerContext jsonContext = null;
77-
var serializationOptions = new HttpResultSerializationOptions { Format = serializationFormat, Version = serializationVersion, JsonContext = jsonContext };
76+
var serializationOptions = new HttpResultSerializationOptions { Format = serializationFormat, Version = serializationVersion, Serializer = serializer };
7877
var response = httpResults.Serialize(serializationOptions);
7978
return response;
8079
}

Libraries/test/Amazon.Lambda.Annotations.SourceGenerators.Tests/Snapshots/CustomizeResponseExamples_NotFoundResponseWithHeaderV1_Generated.g.cs

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -73,8 +73,7 @@ public System.IO.Stream NotFoundResponseWithHeaderV1(Amazon.Lambda.APIGatewayEve
7373
var httpResults = customizeResponseExamples.NotFoundResponseWithHeaderV1(x, __context__);
7474
HttpResultSerializationOptions.ProtocolFormat serializationFormat = HttpResultSerializationOptions.ProtocolFormat.HttpApi;
7575
HttpResultSerializationOptions.ProtocolVersion serializationVersion = HttpResultSerializationOptions.ProtocolVersion.V1;
76-
System.Text.Json.Serialization.JsonSerializerContext jsonContext = null;
77-
var serializationOptions = new HttpResultSerializationOptions { Format = serializationFormat, Version = serializationVersion, JsonContext = jsonContext };
76+
var serializationOptions = new HttpResultSerializationOptions { Format = serializationFormat, Version = serializationVersion, Serializer = serializer };
7877
var response = httpResults.Serialize(serializationOptions);
7978
return response;
8079
}

Libraries/test/Amazon.Lambda.Annotations.SourceGenerators.Tests/Snapshots/CustomizeResponseExamples_NotFoundResponseWithHeaderV2Async_Generated.g.cs

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -73,8 +73,7 @@ public CustomizeResponseExamples_NotFoundResponseWithHeaderV2Async_Generated()
7373
var httpResults = await customizeResponseExamples.NotFoundResponseWithHeaderV2Async(x, __context__);
7474
HttpResultSerializationOptions.ProtocolFormat serializationFormat = HttpResultSerializationOptions.ProtocolFormat.HttpApi;
7575
HttpResultSerializationOptions.ProtocolVersion serializationVersion = HttpResultSerializationOptions.ProtocolVersion.V2;
76-
System.Text.Json.Serialization.JsonSerializerContext jsonContext = null;
77-
var serializationOptions = new HttpResultSerializationOptions { Format = serializationFormat, Version = serializationVersion, JsonContext = jsonContext };
76+
var serializationOptions = new HttpResultSerializationOptions { Format = serializationFormat, Version = serializationVersion, Serializer = serializer };
7877
var response = httpResults.Serialize(serializationOptions);
7978
return response;
8079
}

Libraries/test/Amazon.Lambda.Annotations.SourceGenerators.Tests/Snapshots/CustomizeResponseExamples_NotFoundResponseWithHeaderV2_Generated.g.cs

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -73,8 +73,7 @@ public System.IO.Stream NotFoundResponseWithHeaderV2(Amazon.Lambda.APIGatewayEve
7373
var httpResults = customizeResponseExamples.NotFoundResponseWithHeaderV2(x, __context__);
7474
HttpResultSerializationOptions.ProtocolFormat serializationFormat = HttpResultSerializationOptions.ProtocolFormat.HttpApi;
7575
HttpResultSerializationOptions.ProtocolVersion serializationVersion = HttpResultSerializationOptions.ProtocolVersion.V2;
76-
System.Text.Json.Serialization.JsonSerializerContext jsonContext = null;
77-
var serializationOptions = new HttpResultSerializationOptions { Format = serializationFormat, Version = serializationVersion, JsonContext = jsonContext };
76+
var serializationOptions = new HttpResultSerializationOptions { Format = serializationFormat, Version = serializationVersion, Serializer = serializer };
7877
var response = httpResults.Serialize(serializationOptions);
7978
return response;
8079
}

0 commit comments

Comments
 (0)