Skip to content

Commit 109c89d

Browse files
committed
Tests
1 parent a36c4c8 commit 109c89d

File tree

238 files changed

+9688
-5
lines changed

Some content is hidden

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

238 files changed

+9688
-5
lines changed
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
# Intent Architect
2+
3+
**/.intent/*
4+
!*.application.output.log
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
<Project Sdk="Microsoft.NET.Sdk">
2+
3+
<PropertyGroup>
4+
<TargetFramework>net8.0</TargetFramework>
5+
<OutputType>Library</OutputType>
6+
<GenerateDocumentationFile>true</GenerateDocumentationFile>
7+
<NoWarn>$(NoWarn);1591</NoWarn>
8+
<Nullable>enable</Nullable>
9+
<AWSProjectType>Lambda</AWSProjectType>
10+
<PublishReadyToRun>true</PublishReadyToRun>
11+
</PropertyGroup>
12+
13+
<ItemGroup>
14+
<ProjectReference Include="..\AwsLambdaFunction.Sqs.GroupA.Application\AwsLambdaFunction.Sqs.GroupA.Application.csproj" />
15+
<ProjectReference Include="..\AwsLambdaFunction.Sqs.GroupA.Infrastructure\AwsLambdaFunction.Sqs.GroupA.Infrastructure.csproj" />
16+
</ItemGroup>
17+
<ItemGroup>
18+
<FrameworkReference Include="Microsoft.AspNetCore.App" />
19+
</ItemGroup>
20+
<ItemGroup>
21+
<PackageReference Include="Amazon.Lambda.Annotations" Version="1.7.0" />
22+
<PackageReference Include="Amazon.Lambda.APIGatewayEvents" Version="2.7.1" />
23+
<PackageReference Include="Amazon.Lambda.Core" Version="2.7.1" />
24+
<PackageReference Include="Amazon.Lambda.Logging.AspNetCore" Version="4.1.1" />
25+
<PackageReference Include="Amazon.Lambda.Serialization.SystemTextJson" Version="2.4.4" />
26+
</ItemGroup>
27+
<ItemGroup>
28+
<Content Remove="appsettings.json" />
29+
</ItemGroup>
30+
31+
<ItemGroup>
32+
<Content Include="appsettings.json">
33+
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
34+
</Content>
35+
</ItemGroup>
36+
37+
</Project>
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
using AwsLambdaFunction.Sqs.GroupA.Api.Services;
2+
using AwsLambdaFunction.Sqs.GroupA.Application.Common.Interfaces;
3+
using Intent.RoslynWeaver.Attributes;
4+
using Microsoft.Extensions.Configuration;
5+
using Microsoft.Extensions.DependencyInjection;
6+
7+
[assembly: DefaultIntentManaged(Mode.Fully)]
8+
[assembly: IntentTemplate("Intent.Application.Identity.ApplicationSecurityConfiguration", Version = "1.0")]
9+
10+
namespace AwsLambdaFunction.Sqs.GroupA.Api.Configuration
11+
{
12+
public static class ApplicationSecurityConfiguration
13+
{
14+
public static IServiceCollection ConfigureApplicationSecurity(
15+
this IServiceCollection services,
16+
IConfiguration configuration)
17+
{
18+
services.AddSingleton<ICurrentUserService, CurrentUserService>();
19+
return services;
20+
}
21+
}
22+
}
Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using System.Threading;
4+
using System.Threading.Tasks;
5+
using Amazon.Lambda.Annotations;
6+
using Amazon.Lambda.Annotations.APIGateway;
7+
using Amazon.Lambda.Core;
8+
using AwsLambdaFunction.Sqs.GroupA.Api.Helpers;
9+
using AwsLambdaFunction.Sqs.GroupA.Application.CreateClient;
10+
using AwsLambdaFunction.Sqs.GroupA.Application.CreateOrder;
11+
using Intent.RoslynWeaver.Attributes;
12+
using MediatR;
13+
using Microsoft.Extensions.DependencyInjection;
14+
using Microsoft.Extensions.Logging;
15+
16+
[assembly: DefaultIntentManaged(Mode.Fully)]
17+
[assembly: IntentTemplate("Intent.Aws.Lambda.Functions.LambdaFunctionClassTemplate", Version = "1.0")]
18+
19+
namespace AwsLambdaFunction.Sqs.GroupA.Api
20+
{
21+
public class DefaultFunctions
22+
{
23+
private readonly ILogger<DefaultFunctions> _logger;
24+
private readonly ISender _mediator;
25+
26+
public DefaultFunctions(ILogger<DefaultFunctions> logger, ISender mediator)
27+
{
28+
_logger = logger;
29+
_mediator = mediator ?? throw new ArgumentNullException(nameof(mediator));
30+
}
31+
32+
[LambdaFunction]
33+
[HttpApi(LambdaHttpMethod.Post, "/api/client")]
34+
public async Task<IHttpResult> CreateClientAsync([FromBody] CreateClientCommand command)
35+
{
36+
// AWSLambda0107: can parameter of type System.Threading.CancellationToken passing is not supported.
37+
var cancellationToken = CancellationToken.None;
38+
return await ExceptionHandlerHelper.ExecuteAsync(async () =>
39+
{
40+
await _mediator.Send(command, cancellationToken);
41+
return HttpResults.Created();
42+
}, _logger);
43+
}
44+
45+
[LambdaFunction]
46+
[HttpApi(LambdaHttpMethod.Post, "/api/order")]
47+
public async Task<IHttpResult> CreateOrderAsync([FromBody] CreateOrderCommand command)
48+
{
49+
// AWSLambda0107: can parameter of type System.Threading.CancellationToken passing is not supported.
50+
var cancellationToken = CancellationToken.None;
51+
return await ExceptionHandlerHelper.ExecuteAsync(async () =>
52+
{
53+
await _mediator.Send(command, cancellationToken);
54+
return HttpResults.Created();
55+
}, _logger);
56+
}
57+
}
58+
}
Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using System.Net;
4+
using System.Threading.Tasks;
5+
using Amazon.Lambda.Annotations.APIGateway;
6+
using AwsLambdaFunction.Sqs.GroupA.Application.Common.Exceptions;
7+
using FluentValidation;
8+
using Intent.RoslynWeaver.Attributes;
9+
using Microsoft.Extensions.Logging;
10+
11+
[assembly: DefaultIntentManaged(Mode.Fully)]
12+
[assembly: IntentTemplate("Intent.Aws.Lambda.Functions.ExceptionHandlerHelper", Version = "1.0")]
13+
14+
namespace AwsLambdaFunction.Sqs.GroupA.Api.Helpers
15+
{
16+
public static class ExceptionHandlerHelper
17+
{
18+
public static async Task<IHttpResult> ExecuteAsync(Func<Task<IHttpResult>> operation, ILogger logger)
19+
{
20+
try
21+
{
22+
return await operation();
23+
}
24+
catch (Exception ex)
25+
{
26+
logger.LogError(ex, "Unhandled exception occurred: {Message}", ex.Message);
27+
return HandleException(ex);
28+
}
29+
}
30+
31+
private static IHttpResult HandleException(Exception exception)
32+
{
33+
switch (exception)
34+
{
35+
case ValidationException validationEx:
36+
var errors = new List<ValidationError>();
37+
38+
foreach (var error in validationEx.Errors)
39+
{
40+
errors.Add(new ValidationError(error.PropertyName, error.ErrorMessage, error.ErrorCode));
41+
}
42+
return HttpResults.BadRequest(new ResponseErrors("ValidationError", "One or more validation errors occurred", 400, errors));
43+
case ForbiddenAccessException forbiddenEx:
44+
return HttpResults.NewResult(HttpStatusCode.Forbidden, new ResponseDetail("Forbidden", "Access forbidden", 403, forbiddenEx.Message ?? "You do not have permission to access this resource"));
45+
case UnauthorizedAccessException unauthorizedEx:
46+
return HttpResults.NewResult(HttpStatusCode.Unauthorized, new ResponseDetail("Unauthorized", "Authentication required", 401, unauthorizedEx.Message ?? "Authentication is required to access this resource"));
47+
default:
48+
return HttpResults.NewResult(HttpStatusCode.InternalServerError, new ResponseDetail("InternalServerError", "An error occurred while processing your request", 500, "Please try again later or contact support if the problem persists"));
49+
}
50+
}
51+
52+
private record ResponseDetail(string Type, string Title, int Status, object Detail);
53+
54+
private record ResponseErrors(string Type, string Title, int Status, List<ValidationError> Errors);
55+
56+
private record ValidationError(string Property, string Message, string Code);
57+
58+
}
59+
}
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
{
2+
"profiles": {
3+
"Mock Lambda Test Tool": {
4+
"commandName": "Executable",
5+
"launchBrowser": false,
6+
"commandLineArgs": "--port 5050",
7+
"executablePath": "%USERPROFILE%\\.dotnet\\tools\\dotnet-lambda-test-tool-8.0.exe",
8+
"publishAllPorts": false,
9+
"workingDirectory": "$(ProjectDir)$(OutputPath)"
10+
}
11+
}
12+
}
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
### Overview
2+
3+
This is a .NET AWS Lambda project that leverages the **Amazon.Lambda.Annotations** framework to streamline the creation of serverless functions. This approach allows for idiomatic C# coding patterns, with boilerplate code and CloudFormation templates generated automatically at compile time using C# source generators. The project is configured to use **Amazon.Lambda.Logging.AspNetCore** for CloudWatch logging and can be configured through both **`appsettings.json`** and environment variables.
4+
5+
***
6+
7+
### Local Development and Debugging
8+
9+
You have two primary options for local development and debugging:
10+
11+
#### 1. Running the Mock Lambda Test Tool
12+
13+
The project is configured to launch the **`dotnet-lambda-test-tool-8.0.exe`** from a launch profile in the **`launchSettings.json`** file. This tool provides a local host for running and testing your Lambda functions. The tool allows you to manually invoke your functions with different event payloads, which is useful for rapid, isolated testing.
14+
15+
#### 2. Using the SAM CLI
16+
17+
For a more comprehensive local emulation of the AWS environment, you can use the **AWS SAM CLI**. This is particularly useful for testing how your functions integrate with other AWS services, such as API Gateway.
18+
19+
1. **Install AWS CLI**: If you don't have it, follow the instructions on [Installing or updating to the latest version of the AWS CLI](https://docs.aws.amazon.com/cli/latest/userguide/getting-started-install.html).
20+
2. **Install AWS SAM CLI**: Follow the instructions on [Installing the AWS SAM CLI](https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/install-sam-cli.html).
21+
3. **Build Project**: From the project directory, run the command **`sam build --template serverless.template`**. This will compile your application and prepare it for local execution.
22+
4. **Start the Local API**: From the project directory, run the command **`sam local start-api`**. This will launch a local API Gateway endpoint that routes requests to your Lambda functions, as defined in the CloudFormation template.
23+
24+
***
25+
26+
### Configuration
27+
28+
Application configuration is handled using a standard .NET configuration provider, which reads from **`appsettings.json`** and environment variables. Environment variables take precedence over settings in the `appsettings.json` file, allowing you to easily manage different configurations for various environments (e.g., development, staging, production) without changing code.
29+
30+
***
31+
32+
### Deployment
33+
34+
To deploy the application to AWS, you'll use the **Amazon.Lambda.Tools** global tool.
35+
36+
1. **Prerequisites**:
37+
- If you don't have an AWS account, register at [AWS Console](https://aws.amazon.com/console/).
38+
- Ensure you have an IAM user with appropriate permissions and a configured AWS credentials file at `~/.aws/credentials`.
39+
2. **Install the Deployment Tool**: If not already installed, run `dotnet tool install --global Amazon.Lambda.Tools`.
40+
3. **Deploy**: From your project directory, execute the command **`dotnet lambda deploy-serverless`**. This command will package your application and deploy it to AWS as a CloudFormation stack.
41+
42+
***
43+
44+
### Resources
45+
46+
* **AWS Lambda Annotations GitHub Repository**: The official source code and detailed documentation for the framework.
47+
* [https://github.com/aws/aws-lambda-dotnet/blob/master/Libraries/src/Amazon.Lambda.Annotations/README.md](https://github.com/aws/aws-lambda-dotnet/blob/master/Libraries/src/Amazon.Lambda.Annotations/README.md)
48+
* **AWS Extensions for .NET CLI**: Documentation for the deployment tools.
49+
* [https://github.com/aws/aws-extensions-for-dotnet-cli](https://github.com/aws/aws-extensions-for-dotnet-cli)
50+
* **AWS Lambda for .NET**: The main GitHub repository for the AWS .NET Lambda tools.
51+
* [https://github.com/aws/aws-lambda-dotnet](https://github.com/aws/aws-lambda-dotnet)
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
using Intent.RoslynWeaver.Attributes;
2+
3+
[assembly: DefaultIntentManaged(Mode.Fully)]
4+
[assembly: IntentTemplate("Intent.Aws.Lambda.Functions.JsonResponse", Version = "1.0")]
5+
6+
namespace AwsLambdaFunction.Sqs.GroupA.Api.ResponseTypes
7+
{
8+
/// <summary>
9+
/// Implicit wrapping of types that serialize to non-complex values.
10+
/// </summary>
11+
/// <typeparam name="T">Types such as string, Guid, int, long, etc.</typeparam>
12+
public class JsonResponse<T>
13+
{
14+
public JsonResponse(T value)
15+
{
16+
Value = value;
17+
}
18+
19+
public T Value { get; set; }
20+
}
21+
}
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
using System.Threading.Tasks;
2+
using AwsLambdaFunction.Sqs.GroupA.Application.Common.Interfaces;
3+
using Intent.RoslynWeaver.Attributes;
4+
5+
[assembly: DefaultIntentManaged(Mode.Fully)]
6+
[assembly: IntentTemplate("Intent.Application.Identity.CurrentUserService", Version = "1.0")]
7+
8+
namespace AwsLambdaFunction.Sqs.GroupA.Api.Services
9+
{
10+
public class CurrentUserService : ICurrentUserService
11+
{
12+
public CurrentUserService()
13+
{
14+
}
15+
16+
public Task<ICurrentUser?> GetAsync()
17+
{
18+
return Task.FromResult<ICurrentUser?>(null);
19+
}
20+
21+
public async Task<bool> AuthorizeAsync(string policy)
22+
{
23+
return await Task.FromResult(true);
24+
}
25+
26+
public async Task<bool> IsInRoleAsync(string role)
27+
{
28+
return await Task.FromResult(true);
29+
}
30+
}
31+
}
Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using System.Threading;
4+
using System.Threading.Tasks;
5+
using Amazon.Lambda.Annotations;
6+
using Amazon.Lambda.Core;
7+
using Amazon.Lambda.SQSEvents;
8+
using AwsLambdaFunction.Sqs.GroupA.Application.Common.Eventing;
9+
using AwsLambdaFunction.Sqs.GroupA.Infrastructure.Eventing;
10+
using Intent.RoslynWeaver.Attributes;
11+
using Microsoft.Extensions.Logging;
12+
13+
[assembly: DefaultIntentManaged(Mode.Fully)]
14+
[assembly: IntentTemplate("Intent.Aws.Lambda.Functions.Sqs.LambdaFunctionConsumer", Version = "1.0")]
15+
16+
namespace AwsLambdaFunction.Sqs.GroupA.Api
17+
{
18+
public class SpecificTopicMessageConsumer
19+
{
20+
private readonly ILogger<SpecificTopicMessageConsumer> _logger;
21+
private readonly ISqsMessageDispatcher _dispatcher;
22+
private readonly IEventBus _eventBus;
23+
private readonly IServiceProvider _serviceProvider;
24+
25+
public SpecificTopicMessageConsumer(ILogger<SpecificTopicMessageConsumer> logger,
26+
ISqsMessageDispatcher dispatcher,
27+
IEventBus eventBus,
28+
IServiceProvider serviceProvider)
29+
{
30+
_logger = logger ?? throw new ArgumentNullException(nameof(logger));
31+
_dispatcher = dispatcher ?? throw new ArgumentNullException(nameof(dispatcher));
32+
_eventBus = eventBus ?? throw new ArgumentNullException(nameof(eventBus));
33+
_serviceProvider = serviceProvider ?? throw new ArgumentNullException(nameof(serviceProvider));
34+
}
35+
36+
[LambdaFunction]
37+
public async Task ProcessAsync(SQSEvent sqsEvent, ILambdaContext context)
38+
{
39+
// AWSLambda0107: passing CancellationToken parameters is not supported; use CancellationToken.None instead.
40+
var cancellationToken = CancellationToken.None;
41+
42+
foreach (var record in sqsEvent.Records)
43+
{
44+
try
45+
{
46+
await _dispatcher.DispatchAsync(_serviceProvider, record, cancellationToken);
47+
await _eventBus.FlushAllAsync(cancellationToken);
48+
}
49+
catch (Exception ex)
50+
{
51+
_logger.LogError(ex, "Error processing SpecificTopicMessageConsumer message with ID {MessageId}", record.MessageId);
52+
throw;
53+
}
54+
}
55+
}
56+
}
57+
}

0 commit comments

Comments
 (0)