Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

chore: Idempotency - refactor example to be simpler with a comparable idempotent output #342

Merged
merged 1 commit into from
Jul 14, 2023
Merged
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
67 changes: 11 additions & 56 deletions examples/Idempotency/src/HelloWorld/Function.cs
Original file line number Diff line number Diff line change
Expand Up @@ -15,16 +15,13 @@

using System;
using System.Collections.Generic;
using System.Net.Http;
using System.Text.Json;
using System.Text.Json.Serialization;
using System.Threading.Tasks;
using Amazon.DynamoDBv2;
using Amazon.Lambda.APIGatewayEvents;
using Amazon.Lambda.Core;
using Amazon.Lambda.Serialization.SystemTextJson;
using AWS.Lambda.Powertools.Idempotency;
using AWS.Lambda.Powertools.Idempotency.Persistence;
using AWS.Lambda.Powertools.Logging;

// Assembly attribute to enable the Lambda function's JSON input to be converted into a .NET class.
Expand All @@ -34,34 +31,30 @@ namespace HelloWorld;

public class Function
{
private static HttpClient? _httpClient;
private static AmazonDynamoDBClient? _dynamoDbClient;

/// <summary>
/// Function constructor
/// </summary>
public Function()
{
_httpClient = new HttpClient();
_dynamoDbClient = new AmazonDynamoDBClient();

Init(_dynamoDbClient, _httpClient);
Init(_dynamoDbClient);
}

/// <summary>
/// Test constructor
/// </summary>
public Function(AmazonDynamoDBClient amazonDynamoDb, HttpClient httpClient)
public Function(AmazonDynamoDBClient amazonDynamoDb)
{
_httpClient = httpClient;
_dynamoDbClient = amazonDynamoDb;
Init(amazonDynamoDb, httpClient);
Init(amazonDynamoDb);
}
private void Init(AmazonDynamoDBClient amazonDynamoDb, HttpClient httpClient)

private void Init(AmazonDynamoDBClient amazonDynamoDb)
{
ArgumentNullException.ThrowIfNull(amazonDynamoDb);
ArgumentNullException.ThrowIfNull(httpClient);
var tableName = Environment.GetEnvironmentVariable("TABLE_NAME");
ArgumentNullException.ThrowIfNull(tableName);

Expand Down Expand Up @@ -92,29 +85,13 @@ private void Init(AmazonDynamoDBClient amazonDynamoDb, HttpClient httpClient)
[Logging(LogEvent = true)]
public async Task<APIGatewayProxyResponse> FunctionHandler(APIGatewayProxyRequest apigwProxyEvent, ILambdaContext context)
{
var serializationOptions = new JsonSerializerOptions
{
PropertyNameCaseInsensitive = true
};
var request = JsonSerializer.Deserialize<LookupRequest>(apigwProxyEvent.Body, serializationOptions);
if (request is null)
{
return new APIGatewayProxyResponse
{
Body = "Invalid request",
StatusCode = 403,
Headers = new Dictionary<string, string> { { "Content-Type", "application/json" } }
};
}

var location = await GetCallingIp(request.Address);

var requestContextRequestId = apigwProxyEvent.RequestContext.RequestId;
var response = new
{
RequestId = requestContextRequestId,
Greeting = "Hello Powertools for AWS Lambda (.NET)",
IpAddress = location
MethodGuid = GenerateGuid(), // Guid generated by the GenerateGuid method. used to compare Method output
HandlerGuid = Guid.NewGuid().ToString() // Guid generated in the Handler. used to compare Handler output
};

try
Expand All @@ -138,33 +115,11 @@ public async Task<APIGatewayProxyResponse> FunctionHandler(APIGatewayProxyReques
}

/// <summary>
/// Calls location api to return IP address
/// Generates a new Guid to check if value is the same between calls (should be when idempotency enabled)
/// </summary>
/// <param name="address">Uri of the service providing the calling IP</param>
/// <returns>IP address string</returns>
private static async Task<string?> GetCallingIp(string address)
{
if (_httpClient == null) return "0.0.0.0";
_httpClient.DefaultRequestHeaders.Accept.Clear();
_httpClient.DefaultRequestHeaders.Add("User-Agent", "AWS Lambda .Net Client");

var response = await _httpClient.GetStringAsync(address).ConfigureAwait(false);
var ip = response.Replace("\n", "");

return ip;
}
}

/// <summary>
/// Record to represent the data structure of Lookup request
/// </summary>
[Serializable]
public class LookupRequest
{
public string Address { get; private set; }

public LookupRequest(string address)
/// <returns>GUID</returns>
private static string GenerateGuid()
{
Address = address;
return Guid.NewGuid().ToString();
}
}
6 changes: 3 additions & 3 deletions examples/Idempotency/src/HelloWorld/HelloWorld.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,10 @@
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Amazon.Lambda.Core" Version="2.1.0" />
<PackageReference Include="Amazon.Lambda.APIGatewayEvents" Version="2.5.0" />
<PackageReference Include="Amazon.Lambda.Serialization.SystemTextJson" Version="2.3.0" />
<PackageReference Include="Amazon.Lambda.APIGatewayEvents" Version="2.6.0" />
<PackageReference Include="Amazon.Lambda.Serialization.SystemTextJson" Version="2.3.1" />
<PackageReference Include="AWS.Lambda.Powertools.Idempotency" Version="0.0.1-preview" />
<PackageReference Include="AWS.Lambda.Powertools.Logging" Version="1.1.1" />
<PackageReference Include="AWSSDK.DynamoDBv2" Version="3.7.103.7" />
<PackageReference Include="AWSSDK.DynamoDBv2" Version="3.7.105.1" />
</ItemGroup>
</Project>
42 changes: 7 additions & 35 deletions examples/Idempotency/test/HelloWorld.Test/FunctionTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -53,28 +53,14 @@ public async Task TestHelloWorldFunctionHandler()
// arrange
var requestId = Guid.NewGuid().ToString("D");
var accountId = Guid.NewGuid().ToString("D");
var location = "192.158.1.38";

Environment.SetEnvironmentVariable("POWERTOOLS_SERVICE_NAME","powertools-dotnet-idempotency-sample");
Environment.SetEnvironmentVariable("POWERTOOLS_LOG_LEVEL","INFO");
Environment.SetEnvironmentVariable("TABLE_NAME",_tableName);

var handlerMock = new Mock<HttpMessageHandler>();
handlerMock
.Protected()
.Setup<Task<HttpResponseMessage>>(
"SendAsync",
ItExpr.IsAny<HttpRequestMessage>(),
ItExpr.IsAny<CancellationToken>()
)
.ReturnsAsync(new HttpResponseMessage
{
StatusCode = HttpStatusCode.OK,
Content = new StringContent(location)
});

var request = new APIGatewayProxyRequest
{
Body = "{\"address\": \"https://checkip.amazonaws.com\"}",
Body = "{\"address\": \"Hello World\"}",
RequestContext = new APIGatewayProxyRequest.ProxyRequestContext
{
RequestId = requestId,
Expand All @@ -89,25 +75,11 @@ public async Task TestHelloWorldFunctionHandler()
MemoryLimitInMB = 215,
AwsRequestId = Guid.NewGuid().ToString("D")
};

var body = new Dictionary<string, string>
{
{ "RequestId", requestId },
{ "Greeting", "Hello Powertools for AWS Lambda (.NET)" },
{ "IpAddress", location },
};

var expectedResponse = new APIGatewayProxyResponse
{
Body = JsonSerializer.Serialize(body),
StatusCode = 200,
Headers = new Dictionary<string, string> { { "Content-Type", "application/json" } }
};

// act
var function = new Function(_client, new HttpClient(handlerMock.Object));
var function = new Function(_client);

var firstResponse = await function.FunctionHandler(request, context);
await function.FunctionHandler(request, context);

var secondCallContext = new TestLambdaContext
{
Expand All @@ -116,16 +88,16 @@ public async Task TestHelloWorldFunctionHandler()
MemoryLimitInMB = 215,
AwsRequestId = Guid.NewGuid().ToString("D")
};

var secondResponse = await function.FunctionHandler(request, secondCallContext);

_testOutputHelper.WriteLine("First Response: \n" + firstResponse.Body);
_testOutputHelper.WriteLine("Second Response: \n" + secondResponse.Body);

// assert
Assert.Equal(firstResponse.Body, secondResponse.Body);
Assert.Equal(expectedResponse.Body, secondResponse.Body);
Assert.Equal(expectedResponse.Headers, secondResponse.Headers);
Assert.Equal(expectedResponse.StatusCode, secondResponse.StatusCode);
Assert.Equal(firstResponse.Headers, secondResponse.Headers);
Assert.Equal(firstResponse.StatusCode, secondResponse.StatusCode);
}

}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,10 @@
<PackageReference Include="Amazon.Lambda.Core" Version="2.1.0" />
<PackageReference Include="Amazon.Lambda.TestUtilities" Version="2.0.0" />
<PackageReference Include="Amazon.Lambda.Serialization.SystemTextJson" Version="2.3.1" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.6.2" />
<PackageReference Include="Moq" Version="4.18.3" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.6.3" />
<PackageReference Include="Moq" Version="4.18.4" />
<PackageReference Include="xunit" Version="2.4.2" />
<PackageReference Include="Testcontainers" Version="3.2.0" />
<PackageReference Include="Testcontainers" Version="3.3.0" />
<PackageReference Include="xunit.runner.visualstudio" Version="2.4.5">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
Expand Down