:toc: = Templates toc::[] == WebAPI Template This section contains all of the information about the template that corresponds to the Devon4net WebAPI. This template will allow you to easily create a functional API with everything set up so you can start coding for your specific domain. Firstly, all the details about the contents of the package will be explained. Going through all of the directory tree explaining the most imporant concepts. Then we will show an example of how to add the entity Student from the domain layer all the way to the controller, so you can understand how the architecture works on this template and how the layers interact with each other. === Devon4Net.Application.WebAPI This is the startup project of the template. Its main purpose is to launch the api and configure it using the settings files. The following figure shows the directory tree you will find when you first use the template. .Devon4Net.Application.WebAPI Directory tree image::images/Application.WebAPI_tree.png[] Everything will be set up in the `Program.cs` file as for any .Net 6 Core API project. Going through this file, you will find all the configuration included with the template, such as middlewares and Dependency Injection. ==== appsettings.json [source, json] ---- { "devonfw": { "UseDetailedErrorsKey": true, "UseIIS": false, "UseSwagger": true, "UseXsrf": true, "UseModelStateValidation": true, "Environment": "Development", "ForceUseHttpsRedirection": false, "Kestrel": { "UseHttps": false, "HttpProtocol": "Http1AndHttp2", "ApplicationPort": 8085, "SslProtocol": "Tls12", "ExtraSettings": { "KeepAliveTimeout": 120, "MaxConcurrentConnections": 100, "MaxConcurrentUpgradedConnections": 100, "MaxRequestBodySize": 28.6, "Http2MaxStreamsPerConnection": 100, "Http2InitialConnectionWindowSize": 131072, "Http2InitialStreamWindowSize": 98304, "AllowSynchronousIO": true } }, "IIS": { "ForwardClientCertificate": true, "AutomaticAuthentication": true, "AuthenticationDisplayName": "" } } } ---- In the following list all the configuration fields are described: //* `UseDetailedErrorsKey`: * `UseIIS`: Boolean that indicates if IIS is used. * `ValidateIssuerSigningKey`: Boolean that controls if validation of the security key that signed the securityToken is called. //* `UseSwagger`: * `UseXsrf`: Boolean that adds Anti Forgery middleware to prevent XSRF attacks. * `UseModelStateValidation`: Boolean that indicates if model validation is called. * `Environment`: Running environment to load the corresponding configuration (Development, Production...) * `ForceUseHttpsRedirection`: Boolean to force adding a middleware that redirects HTTP requests to HTTPS. * `Kestrel`: Kestrel server configuration ** `UseHttps`: Boolean to indicate if the connection needs to be secured via HTTPS. ** `HttpProtocol`: Hypertext protocol used. + |==== |*Protocol* |*Description* |`Http1`| HTTP/1.x (default) |`Http2`| HTTP/2 |`Http1andHttp2`| Both HTTP/1.x and HTTP/2 |`none`| |==== ** `ApplicationPort`: Port number where the API will listen ** `SslProtocol`: SSL encrytion protocol for Https + |==== |*Protocol* |*Description* |`Tls12`| TLS 1.2 (default, needed for Https2) |`Tls13`| TLS 1.3 |`none`| |==== ** `ExtraSettings`: Some extra settings for Kestrel *** `KeepAliveTimeout`: Sets the keep-alive timeout in seconds. *** `MaxConcurrentConnections`: Maximum number of open connections. Null is unlimited. Default is 100. *** `MaxConcurrentUpgradedConnections`: Maximum number of open upgraded connectinos. Null is unlimited. An upgraded connection is one that has been switched from HTTP to another protocol. Default is 100. *** `MaxRequestBodySize`: Maximum allowed size of any request body in MB. Default is 28.6MB. *** `Http2MaxStreamsPerConnection`: Maximum number of concurrent request streams per HTTP/2 connection. Excess streams will be refused. Default is 100. *** `Http2InitialConnectionWindowSize`: Indicates how much request body data the server is willing to receive and buffer at a time aggregated across all requests (streams) per connection. Value must be greater or equal to 65,535 and less than 2^31. Defaults to 128 KB. *** `Http2InitialStreamWindowSize`: Indicates how much request body data the server is willing to receive and buffer at a time per stream. *** `AllowSynchronousIO`: Boolean that controls wether Synchronous IO operations are allowed in requests. * `IIS`: Internet Information Services configuration ** `ForwardClientCertificate`: Populates the ITLSConnectionFeature if the MS-ASPNETCORE-CLIENTCERT request header is present ** `AutomaticAuthentication`: If true the middleware should set HttpContext.User. If false the middleware will only provide an identity when explicitly requested by the AuthenticationScheme. Note Windows Authentication must also be enabled in IIS for this to work. ** `AuthenticationDisplayName`: Sets the display name shown to users on login pages. The default is null For each environment you can have a different configuration if you wish. You will find an `appsettings.Development.json` file where the configuration for the different components used in the template is placed. Please go through the documentation of each component to learn more about how are they configured. === Devon4Net.Application.Implementation This is where all the magic happens. This project holds all the classes needed to implement the API end-points. The following image shows the structure you will find in this project. .Devon4Net.Application.WebAPI.Implementation Directory tree image::images/Application.WebAPI.Implementation_tree.png[] NOTE: Please refer to documentation about Architecture to understand better the proposed architecture which is Onion Architecture. ==== Configuration In this directory the Configuration is placed, you will find all the dependency injection code for this project in the static class `DevonConfiguration`. The code is divided into many private extensions based on their intended use, all of which are called by one main public extension for the `IServiceCollection` interface in the `Program.cs` file. TIP: If you need to segregate chunks of code, don't be afraid to add extra static classes. This will improve reading and it will make it much easier to locate items later. ==== Domain As you may have learned thanks to the architecture document, this is the core of the application. It holds the entities and the contracts for the operations you can execute on them, that last being the repository interfaces. You will be able to find also the different database contexts for the application in the Database directory. These contexts will allow `EntityFramework` to work properly by extending `DbContext` class. .Devon4Net.Application.WebAPI.Implementation Domain layer image::images/Application.WebAPI.Implementation.Domain.png[] The repositories interfaces extend from a generic repository interface `IRepository` available in Devon4Net. As you can see in figure 3, Devon4Net already comes with some examples of implementation for some entities. Please revise them so that you have a better understanding of how they work. ==== Data The Data directory corresponds to the Data Layer and implements all the interfaces contracts. This Repositories extend from a generic repository implementation `Repository` available in Devon4Net. As you can see in the next figure, the template also come with some examples of implementation. .Devon4Net.Application.WebAPI.Implementation Data layer image::images/Application.WebAPI.Implementation.Data.png[] ==== Business The Business directory is where all the application logic is placed, it corresponds to the Business Layer. You will find a lot of implemented examples as shown in the next image. .Devon4Net.Application.WebAPI.Implementation Business layer image::images/Application.WebAPI.Implementation.Business.png[] These are: . *AntiForgeryTokenManagement:* This is an example of how to use the XSRF protection in API controllers. It contains a controller class with a working example. + .AntiForgeryTokenManagement directory image::images/AntiForgeryTokenManagement.png[] . *AuthManagement:* This example shows how to use Authorization in API controllers thanks to Json Web Tokens. It contains a controller class and some response entities needed to return information. + .AuthManagement directory image::images/AuthManagement.png[] . *EmployeeManagement:* This directory contains all the implementation classes for entity type Employee. + .EmployeeManagement directory image::images/EmployeeManagement.png[] . *MediatRManagement:* Example of how implement MediatR for using CQRS pattern. + .MediatRManagement directory image::images/MediatRManagement.png[] . *RabbitMqManagement:* Sample of implementation of RabbitMq queues. + .RabbitMqManagement directory image::images/RabbitMqManagement.png[] . *TodoManagement:* Implementation sample of entity type Todo. + .TodoManagement directory image::images/TodoManagement.png[] === Sample of use NOTE: If you don't know how to install and use this template, please refer to documentation on how to do it. In this part we will be creating CRUD (Create, Read, Update and Delete) operations for the entity type `Student`. Before that, we will delete the samples that come with the template. ==== Step 1: Entity First we will need the object `Student`. For that we will need to create a new `Student.cs` class in `Domain\Entities`: [source, c#] ---- public class Student { public long Id { get; set; } public string Name { get; set; } public string Surname { get; set; } } ---- Our student should have an Identifier, and we will also put some interesting properties, name and surname for example. TIP: Is good practice to override `object` methods: `Equals(object o)`, `GetHashCode()` and `ToString()`. ==== Step 2: Context EntityFramework will need a context to be able to work with Students. Lets create a `StudentContext.cs` class in `Domain\Database`: [source, c#] ---- public class StudentContext : DbContext { public virtual DbSet Students { get; set; } public StudentContext(DbContextOptions options) : base(options) { } } ---- ==== Step 3: Repository contract The repository contract in `Data\RepositoryInterfaces` will allow us to inject the repository and will increase decoupling. It will be implemented in the next step. You can inherit methods from Devon4Net `IRepository` as shown, but we will be also implementing a couple of queries: [source, c#] ---- public interface IStudentRepository : IRepository { Task DeleteById(long id); } ---- Everything should be asynchronous making use of the `Task` class. ==== Step 4: Repository implementation Now we will need a repository implementation for the previous interface. It will be placed in `Data\Repositories`, it will inherit also from Devon4Net generic repository and will implement interface methods: [source, c#] ---- public class StudentRepository : Repository, IStudentRepository { public StudentRepository(StudentContext context) : base(context) { } public async Task DeleteById(long id) { var deleted = await Delete(t => t.Id == id).ConfigureAwait(false); if (deleted) return id; throw new ArgumentException($"The Todo entity {id} has not been deleted."); } } ---- ==== Step 5: StudentDto To increase decoupling, we will create a copy of the entity Student in `Business\Dto` for the Business layer. To make it a little different we will be including the surname in the property `FullName` and it will be separated before being stored in the database and viceversa: [source, c#] ---- public class StudentDto { public long Id { get; set; } public string FullName { get; set; } } ---- We will need also a converter placed in `Business\Converters` so we can transform it in both directions: [source, c#] ---- public static class StudentConverter { public static StudentDto EntityToDto(Student item) { if (item == null) return new StudentDto(); return new StudentDto { Id = item.Id, FullName = item.Name + " " + item.Surname }; } public static Student DtoToEntity(StudentDto item) { if (item == null) return new Student(); return new Student { Id = item.Id, Name = item.FullName.Split(" ")[0], Surname = item.FullName.Split(" ")[1] }; } } ---- ==== Step 6: Service Now we will need a service in `Business\StudentManagement\Service`. The service will execute all the necessary operations. In this case as it is a simple CRUD it will only call the repository. The Devon4Net Service class implements Unit of Work pattern. [source, c#] ---- public interface IStudentService { Task> GetAllStudents(); Task CreateStudent(StudentDto student); Task DeleteStudentById(long id); } ---- [source, c#] ---- public class StudentService: Service, IStudentService { private readonly IStudentRepository _studentRepository; public StudentService(IUnitOfWork uoW) : base(uoW) { _studentRepository = uoW.Repository(); } public async Task> GetAllStudents() { Devon4NetLogger.Debug("GetTodo method from service TodoService"); var result = await _studentRepository.Get().ConfigureAwait(false); return result.Select(StudentConverter.EntityToDto); } public async Task CreateStudent(StudentDto student) { var result = await _studentRepository.Create(StudentConverter.DtoToEntity(student)); return StudentConverter.EntityToDto(result); } public async Task DeleteStudentById(long id) { var todo = await _studentRepository.GetFirstOrDefault(t => t.Id == id).ConfigureAwait(false); if (todo == null) throw new ArgumentException($"The provided Id {id} does not exist"); return await _studentRepository.DeleteById(id).ConfigureAwait(false); } } ---- ==== Step 7: Controller The controller will end up looking like this and needs to be placed in `Business\StudentManagement\Controllers`. It uses the `IStudentService` via dependency injection. [source, c#] ---- [EnableCors("CorsPolicy")] [ApiController] [Route("[controller]")] public class StudentController : ControllerBase { private readonly IStudentService _studentService; public StudentController(IStudentService studentService) { _studentService = studentService; } [HttpGet] [ProducesResponseType(typeof(List), StatusCodes.Status200OK)] [ProducesResponseType(StatusCodes.Status400BadRequest)] [ProducesResponseType(StatusCodes.Status404NotFound)] [ProducesResponseType(StatusCodes.Status500InternalServerError)] public async Task GetStudents() { return Ok(await _studentService.GetAllStudents().ConfigureAwait(false)); } [HttpPost] [ProducesResponseType(typeof(StudentDto), StatusCodes.Status201Created)] [ProducesResponseType(StatusCodes.Status400BadRequest)] [ProducesResponseType(StatusCodes.Status404NotFound)] [ProducesResponseType(StatusCodes.Status500InternalServerError)] public async Task Create(StudentDto student) { var result = await _studentService.CreateStudent(student).ConfigureAwait(false); return StatusCode(StatusCodes.Status201Created, result); } [HttpDelete] [ProducesResponseType(typeof(long), StatusCodes.Status200OK)] [ProducesResponseType(StatusCodes.Status400BadRequest)] [ProducesResponseType(StatusCodes.Status404NotFound)] [ProducesResponseType(StatusCodes.Status500InternalServerError)] public async Task Delete(long id) { return Ok(await _studentService.DeleteStudentById(id).ConfigureAwait(false)); } } ---- ==== Step 8: Dependency Injection Now we are ready to inject the context in `Configuration\DevonConfiguration.cs`. In this example we are creating a database stored in memory: [source, c#] ---- services.SetupDatabase(configuration, "Default", DatabaseType.InMemory).ConfigureAwait(false); ---- Also the repository and the service, so we can use them in the entire solution. [source, c#] ---- services.AddTransient(); services.AddTransient(); ---- ==== Test Now you can run the application and go to the path `swagger/index.html`. NOTE: take a look at your application port and the protocol you configured (http or https). .Swagger test for Student image::images/WebApi_template_swagger.png[] == gRPC Template In this part of the document you will learn what is gRPC and how the devon4net gRPC template works. To help you understand how the template works, we will first go over some general concepts concerning gRPC technology in broad strokes. Then continue describing the contract shared between client and server and how it is formed using Google's Protocol Buffers. And last but not least, both the GrpcClient and the GrpcServer will be explained. === gRPC gRPC is a modern open source high performance Remote Procedure Call (RPC) framework. It is totally independent of the language used on the client side and the server side. In gRPC a client app can directly call a method on a server application. Both parts need to share a file that acts as a contract. This way compatibility is ensured between client and server without taking languages into account. .gRPC diagram image::images/gRPC.png[] On the server side, we need to develop a service that implements the contract shared between both parts and also listens to client calls and process them using this implementation. On the client side, the client can use the methods provided by this contract thanks to the "stub" (gRPC client). This file is the Protocol Buffer (".proto" file)and needs to be both in the client and the server for them to understand each other. === Protocol Buffers Protocol buffers provide a cross-platform mechanism for serializing structured data. It is like a JSON file, but smaller and faster. This protocol buffers are written in an language called `proto3`. The following piece of code describes an example: [source, proto] ---- syntax = "proto3"; option csharp_namespace = "Devon4Net.Application.GrpcClient.Protos"; package greet; // The greeting service definition. service Greeter { // Sends a greeting rpc SayHello (HelloRequest) returns (HelloReply); } // The request message containing the user's name. message HelloRequest { string name = 1; } // The response message containing the greetings. message HelloReply { string message = 1; } ---- The first line, specifies that you are using the `proto3` version. There are other versions such as `proto2`. The option `csharp_namespace` specifies the namespace of the proto file in C#. A message types are definitions of object structures. A message might have different fields, that have: * Type: Type of the field value. It can be any scalar type (int32, double, string...) or composite type (enumerations, other messages...). * Number: Each field has a unique number which is used to identify the field. * Rule: Singular (can have zero or one) or repeated (can be zero or multiple times rpeated) You can also see a service contract, which is called `Greeter` and has a method `SayHello` that accepts a `HelloRequest` and returns a `HelloReply`, both also defined on the file. === Devon4Net.Application.GrpcServer This project will launch the server that will be called from the GrpcClient and will manage the rpc calls to the available methods. The proto file, which is shown in the previous section, defines the contract for the service, that will need to be implemented in this same project. As you can see in the following figure, the proto file is placed in the directory `Protos`. .GrpcServer directory tree image::images/grpc_server.png[] The service `GreeterService`, will need to extend `Greeter.GreeterBase` which is generated automatically thanks to the service named `Greeter` in the proto file. This service will need to override the method `SayHello` defined in the proto file. This method can be overriden from the class `Greeter.GreeterBase` as it was already implemented: [source, c#] ---- [GrpcDevonService] public class GreeterService : Greeter.GreeterBase { public GreeterService() { } public override Task SayHello(HelloRequest request, ServerCallContext context) { return Task.FromResult(new HelloReply { Message = "Hello " + request.Name }); } } ---- Notice the attribute `GrpcDevonService`. All services marked with this attribute will be auto registered in the `Program.cs` when adding the following line: [source, c#] ---- builder.Services.AddGrpc(); . . . app.SetupGrpcServices(new List { "Devon4Net.Application.GrpcServer" }); ---- The list of strings provided as argument is the list of assemblies where devon will look for gRPC services tagged with that attribute. NOTE: Check the component documentation to learn more. === Devon4Net.Application.GrpcClient The client project will access the GrpcServer project end-point thanks to the protocol buffer and the configuration in the `appsettings.{environment}.json`. For trying out the communication between the client and the server, you will find an end-point in the `GrpcGreeterController` class. .GrpcClient directory tree image::images/grpc_client.png[] The configuration is done adding the following options to the settings file: [source, json] ---- "Grpc": { "EnableGrpc": true, "UseDevCertificate": true, "GrpcServer": "https://localhost:5002", "MaxReceiveMessageSize": 16, "RetryPatternOptions": { "MaxAttempts": 5, "InitialBackoffSeconds": 1, "MaxBackoffSeconds": 5, "BackoffMultiplier": 1.5, "RetryableStatus": "Unavailable" } }, ---- NOTE: `GrpcServer` is the direction of the gRPC end-points. To learn more about the component configuration please visit the package documentation. The following line will configure the component to use this configuration in the `Program.cs`: [source, c#] ---- builder.Services.SetupGrpc(builder.Configuration); ---- The greeter controller will use a `GrpcChannel` injected in the constructor as follows: [source, c#] ---- [ApiController] [Route("[controller]")] public class GrpcGreeterClientController : ControllerBase { private GrpcChannel GrpcChannel { get; } public GrpcGreeterClientController(GrpcChannel grpcChannel) { GrpcChannel = grpcChannel; } ---- You will find that the end-point that accesses the gRPC service looks like this: [source, c#] ---- [HttpGet] public async Task Get(string name) { try { var client = new Greeter.GreeterClient(GrpcChannel); return await client.SayHelloAsync(new HelloRequest { Name = name }).ResponseAsync.ConfigureAwait(false); } catch (Exception ex) { Devon4NetLogger.Error(ex); throw; } } ---- The `Greeter.GreeterClient` is created automatically and can be used when adding the reference to the proto file namespace with the `using` directive. === References * https://grpc.io/docs/what-is-grpc/introduction/[Introduction to gRPC - gRPC Docs] * https://developers.google.com/protocol-buffers/docs/overview[Protocol Buffers - Google Docs] == AWS Templates The devon4net stack includes a variety of templates aimed at developing applications with Amazon Web Services (AWS). AWS is the most used cloud computing platform and that is why Devon gives you the resources you need to make designing and developing for this cloud technology much more simpler and efficient. === AWS Introduction Amazon Web Services (AWS) is a collection of cloud computing services that Amazon provides an online scalable and cost-effective platform to developers and companies dedicated to software. It's mainly directed to online services and its the leading cloud provider in the marketplace providing over 100+ services and a platform to manage them all. === Devon4Net.Application.Lambda This package is a template for constructing Lambda Functions for AWS. It makes use of the `Devon4Net.Infrastructure.AWS.Lambda`, so please read the documentation about this component before starting to use the template. You also have a 'How to: AWS Lambda' tutorial that shows you how you can develop a lambda function making use of this template. .lambda Template File structure image::images/aws_template_structure.png[] In the previous figure you can see the whole structure of the package. It has one main directory called `business` where you can find three different examples of Lambda functions that will be explained further on this document. Each of the three management directories is divides as follows: * `Dto`: Here are placed the necessary classes of the custom objects that are used to input and output the data in the lambda functions. * `Functions`: This folder will contain the function definition for the lambda functions. * `Handlers`: In this directory the classes that implement the `FunctionHandler` method that will define the lambda function behaviour. You will also find in the same level as the business directory some configuration files and a `Readme.md` file with some extra information. ==== Configuration The configuration files are the following: ===== appsettings.json This file contains the configuration for all of the components and the project in general. It can contain subversions following the structure `appsettings.{environment}.json` and it contains the following: [source, json] ---- { "AWS": { "UseSecrets": true, "UseParameterStore": true, "Credentials": { "Profile": "", "Region": "eu-west-1", "AccessKeyId": "", "SecretAccessKey": "" } } } ---- * `UseSecrets`: Boolean to indicate if AWS Secrets Manager is being used. * `UseParameterStore`: Boolean to indicate if AWS Parameter Store is being used. * `Credentials`: Credentials for connecting the app with your AWS profile. NOTE: Read the AWS components documentation to learn more about the configuration you can do in this type of files. ===== aws-lambda-tools-defaults.json This file provides default values to the AWS CLI for configuring and deploy with Visual Studio. Run this command to learn more: [source, console] ---- dotnet lambda deploy-serverless --help ---- This is the file: [source, json] ---- { "Information": [ "This file provides default values for the deployment wizard inside Visual Studio and the AWS Lambda commands added to the .NET Core CLI.", "To learn more about the Lambda commands with the .NET Core CLI execute the following command at the command line in the project root directory.", "dotnet lambda help", "All the command line options for the Lambda command can be specified in this file." ], "profile": "default", "region": "eu-west-1", "framework": "net6.0", "configuration": "Release", "s3-prefix": "AWSServerlessTest/", "function-memory-size": 256, "function-timeout": 30, "function-handler": "Devon4Net.Application.Lambda::AWSLambda.Bussiness.StringManagement.UpperFunction::FunctionHandler", "template": "serverless.template" } ---- The `function-handler` option is the function handler location in the form of `Assembly::Namespace.ClassName::MethodName`. And the `template` option will point to a configuration file for the cloud formation. NOTE: Read the AWS documentation to learn more about this configuration. ===== serverless.template This configuration provides all the information to the Cloud System so that you don't have to adjust anything for deploying the function/s. [source, json] ---- { "AWSTemplateFormatVersion": "2010-09-09", "Transform": "AWS::Serverless-2016-10-31", "Description": "An AWS Serverless Application that uses the ASP.NET Core framework running in Amazon Lambda.", "Parameters": {}, "Conditions": {}, "Resources": { "ToUpperFunction": {...}, "ToLowerFunction": {...}, "SnsManagementFunction": {...}, "SqsManagementFunction": {...}, }, "Outputs": { "ApiURL": { "Description": "API endpoint URL for Prod environment", "Value": { "Fn::Sub": "https://${ServerlessRestApi}.execute-api.${AWS::Region}.amazonaws.com/Prod/" } } } } ---- As you will notice, the `Resources` part contains the configuration for each function. This configuration will look like this: [source, json] ---- "ToUpperFunction": { "Type": "AWS::Serverless::Function", "Properties": { "Handler": "Devon4Net.Application.Lambda::Devon4Net.Application.Lambda.Business.StringManagement.Functions.Upper.UpperFunction::FunctionHandler", "Runtime": "dotnet6", "CodeUri": "", "MemorySize": 256, "Timeout": 30, "Role": null, "Policies": [ "AWSLambdaFullAccess", "AmazonSSMReadOnlyAccess", "AWSLambdaVPCAccessExecutionRole" ], "Environment": { "Variables": {} }, "Events": { "ProxyResource": { "Type": "Api", "Properties": { "Path": "/{proxy+}", "Method": "ANY" } }, "RootResource": { "Type": "Api", "Properties": { "Path": "/", "Method": "ANY" } } } } }, ---- The `Properties` section will have the value of the location as we saw on the `aws-lambda-tools-defaults.json` file in the form of `{DLL_NAME}::{NAMESPACE}::{FUNCTION_HANDLER_NAME}`. NOTE: Read the AWS documentation to learn more about this configuration. ==== Samples As you may know as this point this template includes three samples that show you how to develop lambda functions and also how to interact with other services such as SNS and SQS. WARNING: Please go through the code slowly to understand the trace it follows and what it does. ===== SnsManagement Amazon Simple Notification Service (Amazon SNS) is a fully managed messaging service for both application-to-application (A2A) and application-to-person (A2P) communication. This Sample shows you the proper way of handling SNS Events and processing messages in a Lambda Function. ===== SqsManagement Amazon Simple Queue Service (SQS) is a fully managed message queuing service that enables you to decouple and scale microservices, distributed systems, and serverless applications. This Sample shows you to handle and process Messages from an AWS Queue service directly in a Lambda Function ===== StringManagement This is a very simple function that shows how you can perform a lambda to process a string and transform it to uppercase or lowercase. ==== References * https://aws.amazon.com/lambda/[AWS Lambda] * https://docs.aws.amazon.com/code-samples/latest/catalog/lambda_functions-blank-csharp-src-blank-csharp-aws-lambda-tools-defaults.json.html[aws-lamda-tools-defaults.json] * https://docs.aws.amazon.com/sdk-for-net/v3/developer-guide/net-dg-advanced-config.html === Devon4Net.Application.WebAPI.Serverless This package contains a template for building a serverless WebAPI. It makes use of the `Devon4Net.Infrastructure.WebAPI.Serverless` component, therefore please read the documentation about it before using the template. You also have a 'How to: AWS Serverless' guide that demonstrates you how to use this template to create a serverless WebAPI. .webAPI serverless Template File structure image::images/WebAPI-AwsServerless-structure.png[] In the previous figure you can see the whole structure of the package. ==== Configuration The configuration files are the following: ===== appsettings.json It's the configuration file for the component. It can contain subversions following the structure `appsettings.{environment}.json`. [source, json] ---- { "AWS": { "EnableAws": true, "UseSecrets": true, "UseParameterStore": true, "Credentials": { "Profile": "default", "Region": "eu-west-1", "AccessKeyId": "", "SecretAccessKey": "" }, "Cognito": { "IdentityPools": [ { "IdentityPoolId": "", "IdentityPoolName": "", "ClientId": "" } ] }, "SqSQueueList": [ { "QueueName": "", // Mandatory. Put the name of the queue here "Url": "", //optional. If it is not present, it will be requested to AWS "UseFifo": false, "MaximumMessageSize": 256, "NumberOfThreads": 2, "DelaySeconds": 0, "ReceiveMessageWaitTimeSeconds": 0, "MaxNumberOfMessagesToRetrievePerCall": 1, "RedrivePolicy": { "MaxReceiveCount": 1, "RedrivePolicy": { "MaxReceiveCount": 1, "DeadLetterQueueUrl": "" } } } ] } } ---- * `UseSecrets`: Boolean to indicate if AWS Secrets Manager is being used. * `UseParameterStore`: Boolean to indicate if AWS Parameter Store is being used. * `Credentials`: Credentials for connecting the app with your AWS profile. * `Cognito`: Amazon Cognito identity pools provide temporary AWS credentials for users who are guests (unauthenticated) and for users who have been authenticated and received a token. An identity pool is a store of user identity data specific to your account. In this section you can configure multiple IdentityPools. * `SqSQueueList`: This section is used to configure the Amazon Simple Queue Service (SQS). You must configure some parameters about the queue: ** `QueueName`: The name of the queue, this field is required. ** `Url`: The queue's url, this parameter is optional. ** `UseFifo`: We have two queue types in Amazon SQS, use *false* for Standard Queues or set this parameter to *true* for FIFO Queues. ** `MaximumMessageSize`: The maximum message size for this queue. ** `NumberOfThreads`: The number of threads of the queue. ** `DelaySeconds`: The amount of time that Amazon SQS will delay before delivering a message that is added to the queue. ** `ReceiveMessageWaitTimeSeconds`: The maximum amount of time that Amazon SQS waits for messages to become available after the queue gets a receive request. ** `MaxNumberOfMessagesToRetrievePerCall`: The maximum number of messages to retrieve per call. ** `RedrivePolicy`: Defines which source queues can use this queue as the dead-letter queue NOTE: Read the https://docs.aws.amazon.com/AWSSimpleQueueService/latest/SQSDeveloperGuide/sqs-configure-queue-parameters.html[AWS SQS documentation] to learn more about the configuration of this kind of queues. ==== References * https://aws.amazon.com/serverless/?nc1=h_ls[Serverless on AWS] * https://docs.aws.amazon.com/cognito/latest/developerguide/identity-pools.html[Using identity pools] * https://aws.amazon.com/sqs/?nc1=h_ls[Amazon SQS] * https://aws.amazon.com/sqs/features/[Amazon SQS features] * https://docs.aws.amazon.com/AWSSimpleQueueService/latest/SQSDeveloperGuide/sqs-configure-queue-parameters.html[Configuring queue parameters] * https://docs.aws.amazon.com/AWSSimpleQueueService/latest/SQSDeveloperGuide/sqs-delay-queues.html[Amazon SQS delay queues] * https://docs.aws.amazon.com/AWSSimpleQueueService/latest/SQSDeveloperGuide/sqs-dead-letter-queues.html[Amazon SQS dead-letter queues] === Devon4Net.Application.DynamoDb This package contains a template for building a DynamoDb database. It makes use of the `Devon4Net.Infrastructure.DynamoDb` component, therefore please read the documentation about it before using the template. You also have a 'How to: DynamoDb' section that explains how to use the component with some examples. WARNING: This template uses Lambda functions to get and set data into the DynamoDb database. if you don't know about lambda functions, please read the 'How to: AWS Lambda Function' documentation. .DynamoDb Template File structure image::images/aws_dynamo_template_structure.png[] In the previous figure you can see the whole structure of the package: * `Domain`: Here are placed the necessary classes of the custom objects that are used to input and output the data in the lambda functions. The `Entity Repository` is used to manage these objects. * `Dto`: Here are placed the necessary classes of the custom objects that are used to input and output the data in the lambda functions. These objects are managed with the `Table Repository`. * `Functions`: This folder will contain the function definition for the lambda functions. * `Handlers`: In this directory the classes that implement the `FunctionHandler` method that will define the lambda function behaviour. + In this case, we have a function that creates a DynamoDb table if it does not already exist and inserts data into it, it returns the value that was entered into the database. + This has been achieved in two ways: by making use of the *Table repository* (low-level model) or through the use of the *Entity repository* (persistence programming model) NOTE: If you need more information about Table or Entity Repository please check the documentation about the DynamoDb component. ==== Configuration The configuration files are the following: ===== appsettings.json This file contains the configuration for all of the components and the project in general. It can contain subversions following the structure `appsettings.{environment}.json` and it contains the following: [source, json] ---- { "AWS": { "UseSecrets": true, "UseParameterStore": true, "Credentials": { "Profile": "", "Region": "eu-west-1", "AccessKeyId": "", "SecretAccessKey": "" } } } ---- * `UseSecrets`: Boolean to indicate if AWS Secrets Manager is being used. * `UseParameterStore`: Boolean to indicate if AWS Parameter Store is being used. * `Credentials`: Credentials for connecting the app with your AWS profile. ===== aws-lambda-tools-defaults.json This file provides default values to the AWS CLI for configuring and deploy with Visual Studio. You can run this command to learn more: [source, console] ---- dotnet lambda deploy-serverless --help ---- This is the file: [source, json] ---- { "Information": [ "This file provides default values for the deployment wizard inside Visual Studio and the AWS Lambda commands added to the .NET Core CLI.", "To learn more about the Lambda commands with the .NET Core CLI execute the following command at the command line in the project root directory.", "dotnet lambda help", "All the command line options for the Lambda command can be specified in this file." ], "profile": "default", "region": "eu-west-1", "framework": "net6.0", "function-runtime": "dotnet6", "configuration": "Release", "s3-prefix": "AWSServerlessTest/", "function-memory-size": 256, "function-timeout": 30, "function-handler": "Devon4Net.Application.DynamoDb::Devon4Net.Application.DynamoDb.business.DynamoDbManagement.Functions.DynamoDbManagementFunction::FunctionHandler", "template": "serverless.template" } ---- The `function-handler` option is the function handler location in the form of `Assembly::Namespace.ClassName::MethodName`. And the `template` option will point to a configuration file for the cloud formation. NOTE: Read the AWS documentation to learn more about this configuration. ===== serverless.template This configuration sends all of the information to the Cloud System, so you don't have to change anything when deploying the functions. [source, json] ---- { "AWSTemplateFormatVersion": "2010-09-09", "Transform": "AWS::Serverless-2016-10-31", "Description": "An AWS Serverless Application that uses the ASP.NET Core framework running in Amazon Lambda.", "Parameters": {}, "Conditions": {}, "Resources": { "DynamoDbFunction": {...}, "DynamoDbComplesObjectFunctionEventHandler": {...}, }, "Outputs": { "ApiURL": { "Description": "API endpoint URL for Prod environment", "Value": { "Fn::Sub": "https://${ServerlessRestApi}.execute-api.${AWS::Region}.amazonaws.com/Prod/" } } } } ---- As you can see, the `Resources` section includes the configuration for each function. This configuration will appear as follows: [source, json] ---- "DynamoDbFunction": { "Type": "AWS::Serverless::Function", "Properties": { "Handler": "Devon4Net.Application.DynamoDb::Devon4Net.Application.DynamoDb.business.DynamoDbManagement.Functions.DynamoDbManagementFunction::FunctionHandler", "Runtime": "dotnet6", "CodeUri": "", "MemorySize": 256, "Timeout": 30, "Role": null, "Policies": [ "AWSLambdaFullAccess", "AmazonSSMReadOnlyAccess", "AWSLambdaVPCAccessExecutionRole" ], "Environment": { "Variables": {} }, "Events": { "ProxyResource": { "Type": "Api", "Properties": { "Path": "/{proxy+}", "Method": "ANY" } }, "RootResource": { "Type": "Api", "Properties": { "Path": "/", "Method": "ANY" } } } } }, ---- The `Properties` section will contain the value of the location, as seen in the `aws-lambda-tools-defaults.json` file in the form of `Assembly::Namespace.ClassName::MethodName`. ==== References * https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/GettingStarted.html[Getting started with DynamoDB and AWS SDKs] * https://docs.aws.amazon.com/sdk-for-net/v3/developer-guide/dynamodb-intro.html[Using Amazon DynamoDB NoSQL databases] * https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/WorkingWithTables.Basics.html[Basic operations on DynamoDB tables] * https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/SettingUp.DynamoWebService.html[Setting up DynamoDB (web service)]