Giter Site home page Giter Site logo

cythral / lambdajection Goto Github PK

View Code? Open in Web Editor NEW
16.0 3.0 1.0 2 MB

Framework for building AWS Lambdas using dependency injection and aspect-oriented programming.

Home Page: https://cythral.atlassian.net/jira/software/c/projects/LAMBJ/issues

License: MIT License

Makefile 0.11% C# 99.89%
aws aws-lambda dependency-injection csharp aspect-oriented-programming dotnet csharp-sourcegenerator

lambdajection's Introduction

Lambdajection

Nuget Nuget GitHub GitHub Workflow Status Codecov Sponsor on Github Donate on Paypal

Write elegant and testable AWS Lambdas + Lambda-backed custom resources using C#/.NET and Microsoft's extensions for dependency injection, configuration and more. Start from a template, add handler code, inject services and let Lambdajection do the rest!

Why Lambdajection?

  • Easy dependency management: configure dependencies/services using a Startup class, similar to how it is done in ASP.NET Core.
  • Easy secrets management: automatically decrypt options marked as [Encrypted] using KMS or your own provided cryptography service.
  • Faster startup times: we're using code generation over reflection wherever possible to help minimize cold-start times.
  • Highly configurable: customize serialization, configuration, and run custom code before your main handler is invoked.
  • Highly testable: facilitates use of dependency-injection to make testing your Lambda easier.
  • Flexibility: you can use AWS' provided runtime or roll your own runtime containing .NET. Lambdajection works both ways, and can even generate code for running on a custom/provided runtime.

Community contribution/pull requests are welcome and encouraged! See the contributing guide for instructions. Report issues on JIRA - you can report anonymously or include github username/contact info on the ticket.

Table of Contents

1. Installation

1.1. Metapackage

See the packages section for a list of available packages. Starting in v0.5.0-beta2, you will need to have the .NET 5 SDK installed.

dotnet add package Lambdajection

1.2. Templates

dotnet new -i Lambdajection.Templates

1.3. Development Builds

Development builds are generated for PRs and uploaded to GitHub Packages. To use them, update the user config file for nuget (varies by OS - see this article) and add this to the packageSourceCredentials section of that file:

<github>
    <add key="Username" value="USERNAME" />
    <add key="ClearTextPassword" value="TOKEN" />
</github>

Replace USERNAME with your username, and TOKEN with a personal access token from GitHub that has permissions to read packages. It is important that this goes in the user config file rather than the project one, so that you do not accidentally leak your personal access token to the world.

Then, in your project's nuget.config file, add the following to your packageSources section:

<add key="github" value="https://nuget.pkg.github.com/cythral/index.json" />

Finally, you may use development builds by adding the package and version to your .csproj, for instance:

<PackageReference Include="Lambdajection" Version="0.3.0-gc2ca768d3f" />

Browse development builds here.

2. Packages

Lambdajection Nuget Nuget
Lambdajection.Attributes Nuget Nuget
Lambdajection.Core Nuget Nuget
Lambdajection.Generator Nuget Nuget
Lambdajection.Encryption Nuget Nuget
Lambdajection.Templates Nuget Nuget
Lambdajection.Runtime Nuget Nuget
Lambdajection.Layer Nuget Nuget
Lambdajection.Framework Nuget Nuget
Lambdajection.Framework.BuildTime Nuget Nuget
Lambdajection.CustomResource Nuget Nuget
Lambdajection.CustomResource.BuildTime Nuget Nuget

3. Templates

Lambdajection .NET templates are available for your convenience. Run dotnet new [template-name] --help to see a list of available options for each template.

Lambdajection Project

dotnet new lambdajection

Creates a new C# project with Lambdajection installed, plus boilerplate for a Lambda Handler and Startup class.

Options Class

dotnet new lambdajection-options

Creates a new Options class to be injected into your Lambda as an IOption<>.

4. Usage

4.1. Lambda Handler

Writing the lambda is simple: Just define a public, partial class that contains a Handle method and annotate the class with the Lambda attribute. The Lambda attribute requires that you specify a startup class - more on this in the next step. You are not limited to an request/input parameter of type object - this can be any serializable value or reference type. Same goes for the return value, however the return value must be enclosed in a Task.

using System.Threading;
using System.Threading.Tasks;

using Amazon.Lambda.Core;

using Lambdajection.Attributes;

namespace Your.Namespace
{
    [Lambda(typeof(Startup))]
    public partial class YourLambda
    {
        private IRegisteredService yourService;

        public YourLambda(IRegisteredService yourService)
        {
            this.yourService = yourService;
        }

        public Task<object> Handle(object request, CancellationToken cancellationToken)
        {
            return new {
                foo = request
            };
        }
    }
}

4.2. Request Validation Attributes

Validation attributes in the System.ComponentModel.DataAnnotations namespace can be added to request properties, and they will be automatically evaluated against the request before your Lambda runs:

using System.ComponentModel.DataAnnotations;

class Request
{
    [Range(3, 12)]
    public int Age { get; set; }
}

4.3. Startup Class

The startup class configures services that are injected into the Lambda's IoC container / service collection.

  • Use the ConfigureServices method to add services to your lambda.
    • Use IServiceCollection.UseAwsService<IAmazonService> to inject AWS Clients and Client Factories into the lambda. See the example here.
  • Optionally use the ConfigureLogging method to configure additional log settings.
using System;

using Lambdajection.Core;

using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;

namespace Your.Namespace
{
    public class Startup : ILambdaStartup
    {
        public IConfiguration Configuration { get; }

        public Startup(IConfiguration configuration)
        {
            this.Configuration = configuration;
        }

        public void ConfigureServices(IServiceCollection services)
        {
            // configure injected services here
            services.AddScoped<IRegisteredService, DefaultRegisteredService>();

            // Add AWS Services by their interface name - the default
            services.UseAwsService<IAmazonService>();
        }

        public void ConfigureLogging(ILoggingBuilder logging)
        {
            // this method is optional
            // logging comes preconfigured to log to the console
        }
    }
}

4.4. Customizing Configuration

By default, configuration is environment variables-based. If you would like to use a file-based or other configuration scheme, you may supply a custom configuration factory to the Lambda attribute:

[Lambda(typeof(Startup), ConfigFactory = typeof(ConfigFactory))]
public partial class Lambda
{
    ...

A custom config factory might look like the following:

using System.IO;

using Lambdajection.Core;

using Microsoft.Extensions.Configuration;

namespace Your.Namespace
{
    public class ConfigFactory : ILambdaConfigFactory
    {
        public IConfigurationRoot Create()
        {
            return new ConfigurationBuilder()
                .SetBasePath(Directory.GetCurrentDirectory())
                .AddJsonFile("appsettings.json", optional: true)
                .Build();
        }
    }
}

See the full example here.

4.5. Adding Options

You can add an options section by defining a class for that section, and annotating it with the LambdaOptions attribute. If any options are in encrypted form, add the Encrypted attribute to that property. When the options are requested, the IDecryptionService singleton in the container will be used to decrypt those properties. The default decryption service uses KMS to decrypt values.

  • The Encrypted attribute, IDecryptionService and DefaultDecryptionService are all provided by the Lambdajection.Encryption package.
  • Option classes must be in the same assembly as your lambda.
  • You can replace the default decryption service with your own IDecryptionService by injecting it as a singleton in your Startup class' ConfigureServices method.
  • See the example for using Encrypted properties.
using Lambdajection.Encryption;

namespace Your.Namespace
{
    [LambdaOptions(typeof(LambdaHandler), "SectionName")]
    public class ExampleOptions
    {
        public string ExampleValue { get; set; }

        [Encrypted]
        public string ExampleEncryptedValue { get; set; }
    }
}

4.6. Initialization Services

Initialization services can be used to initialize data or perform some task before the lambda is run. Initialization services should implement ILambdaInitializationService and be injected into the container as singletons at startup.

4.7. Disposers

Disposers can be used to cleanup unmanaged resources, such as open file-handles and network connections. Lambdajection supports Lambdas that implement either IDisposable, IAsyncDisposable or both. If you implement both, DisposeAsync will be preferred.

4.8. Handler Scheme

When configuring your lambda on AWS, the method name you'll want to use will be Run (NOT Handle). For context, Run is a static method generated on your class during compilation. It takes care of setting up the IoC container, if it hasn't been setup already.

So, going off the example above, the handler scheme would look like this:

Your.Assembly.Name::Your.Namespace.YourLambda::Run

You can customize the name of the "Run" method via the RunnerMethod property of LambdaAttribute.

4.9. Lambda-Backed Custom Resource Providers

Lambdajection also allows you to write lambda-backed custom resource providers. Just write create, update and delete methods and Lambdajection will take care of deciding which one gets called + respond to CloudFormation.

Just use the Custom Resource Provider Attribute:

using System.Threading.Tasks;

using Lambdajection.Attributes;
using Lambdajection.CustomResource;

namespace Your.Namespace
{
    [CustomResourceProvider(typeof(Startup))]
    public class Handler
    {
        public Task<Response> Create(CustomResourceRequest<Properties> request, CancellationToken cancellationToken)
        {
            ...
        }

        public Task<Response> Update(CustomResourceRequest<Properties> request, CancellationToken cancellationToken)
        {
            ...
        }

        public Task<Response> Delete(CustomResourceRequest<Properties> request, CancellationToken cancellationToken)
        {
            ...
        }
    }

    public class Response : ICustomResourceOutputData
    {
        public string Id { get; set; } // Id becomes the custom resource's PhysicalResourceId.

        /* Any other properties you define here become attributes of the custom resource
        
        ie. !GetAtt Resource.YourProperty */
    }

    public class Properties
    {
        /* If the name changes, the Create method will be called instead of update, since the UpdateRequiresReplacement attribute is used here. */
        [UpdateRequiresReplacement]
        public string Name { get; set; }
    }
}

See also the custom resource example.

4.10. Custom Runtimes

Lambdajection can be used with custom runtimes starting in v0.5.0-beta2, that way you can use a newer version of .NET as soon as it comes out, even if it is not an LTS release.

In order to use custom runtimes, add the Lambdajection.Runtime package to your csproj file. You must also specify the RuntimeIdentifiers property, with at least linux-x64 included:

<Project>
    <PropertyGroup>
        <RuntimeIdentifiers>linux-x64</RuntimeIdentifiers>
    </PropertyGroup>

    <ItemGroup>
        <PackageReference Include="Lambdajection" Version="$(LambdajectionVersion)" />
        <PackageReference Include="Lambdajection.Runtime" Version="$(LambdajectionVersion)" />
    </ItemGroup>
</Project>

You may also optionally set the SelfContained property:

  • Set it to true if you want to deploy as a self-contained package. In this case, Lambdajection will automatically set your assembly name to bootstrap.
  • Set it to false if you want to deploy as a framework dependent package, ie you installed .NET to a Lambda Layer and want to use that to cut-down on deployment package sizes. In this case, your assembly name will remain unchanged.
  • In both cases, a main method / program entrypoint will be generated for you with the aid of Amazon.Lambda.RuntimeSupport.

See an example of a non-self contained lambda using a custom runtime here. (Example documentation coming soon). Note that the example is using a Lambda Layer we deployed with a custom bootstrap file.

4.11. Lambda Layer

You can use a Lambda Layer containing Lambdajection and all of its dependencies to cut down on package sizes. The layer will be available on the serverless application repository. Once deployed, you can use it on functions that use the Lambdajection.Runtime package on custom runtimes containing .NET 5.

To use the layer:

  1. Deploy lambdajection-layer from the Serverless Application Repository to your AWS Account.
    • You must use the same semantic version as the Lambdajection package in your project.
  2. Note the value of the LayerArn output in the resulting stack and add it to your Lambda's list of layers. See the custom runtime example template on how to do this - specifically the CustomRuntime resource's Layers section.
  3. Add the Lambdajection.Runtime and Lambdajection.Layer packages to your project (run dotnet add package Lambdajection.Layer).
    • Make sure to use the same version as the Lambdajection package in your project.
  4. Finally, you will need to set the DOTNET_SHARED_STORE environment variable to /opt/. This is because Lambda Layers are unzipped to that directory, and .NET needs to know where to look for the runtime package store.

5. Examples

6. Acknowledgments

  1. CodeGeneration.Roslyn - Was used for compile-time code generation using attributes in versions v0.1.0 - v0.4.0. Newer versions use .NET 5 Source Generators.
  2. Simple Lambda Dependency Injection in AWS Lambda .NET Core by Gary Woodfine - primary inspiration for this project.

7. Donations

If you use this project and find it useful, please consider donating. Donations are accepted via Github Sponsors and PayPal.

8. Contributing

Issues and feature requests may be reported anonymously on JIRA Cloud. Pull requests are always welcome! See the contributing guide for more information.

9. Security

Security issues can be reported on our JIRA. See our security policy for more information.

10. License

This project is licensed under the MIT License.

lambdajection's People

Contributors

brighid-bot avatar github-actions[bot] avatar talenfisher avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar

Forkers

brighid-bot

lambdajection's Issues

Dependency Dashboard

This issue lists Renovate updates and detected dependencies. Read the Dependency Dashboard docs to learn more.

Rate-Limited

These updates are currently rate-limited. Click on a checkbox below to force their creation now.

  • Update dependency dotnet-sdk to v7.0.406
  • Update kentaro-m/auto-assign-action action to v1.2.6
  • Update dependency Amazon.Lambda.Core to v2.2.0
  • Update dependency Microsoft.Build.Locator to v1.7.1
  • Update dependency Microsoft.NET.Test.Sdk to v17.9.0
  • Update dependency NUnit3TestAdapter to v4.5.0
  • Update dependency Nerdbank.GitVersioning to v3.6.133
  • Update dependency YamlDotNet to v13.7.1
  • Update dependency nbgv to v3.6.133
  • Update dependency nunit to v3.14.0
  • Update nuget monorepo to v6.9.1 (NuGet.Commands, NuGet.Frameworks, NuGet.ProjectModel)
  • Update roslyn monorepo to v4.9.2 (Microsoft.CodeAnalysis, Microsoft.CodeAnalysis.CSharp.CodeStyle, Microsoft.CodeAnalysis.CSharp.Workspaces, Microsoft.CodeAnalysis.Workspaces.MSBuild)
  • Update GitHub Artifact Actions to v4 (major) (actions/download-artifact, actions/upload-artifact)
  • Update actions/checkout action to v4
  • Update actions/setup-dotnet action to v4
  • Update aws-actions/configure-aws-credentials action to v4
  • Update codecov/codecov-action action to v4
  • Update dependency Microsoft.SourceLink.GitHub to v8
  • Update dependency YamlDotNet to v15
  • Update dependency coverlet.collector to v6
  • Update dependency nunit to v4
  • Update dotnet monorepo to v8 (major) (Microsoft.Bcl.AsyncInterfaces, Microsoft.Extensions.Configuration, Microsoft.Extensions.Configuration.EnvironmentVariables, Microsoft.Extensions.Configuration.Json, Microsoft.Extensions.DependencyInjection, Microsoft.Extensions.Hosting, Microsoft.Extensions.Logging, Microsoft.Extensions.Logging.Console, Microsoft.Extensions.Options, Microsoft.Extensions.Options.ConfigurationExtensions, dotnet-sdk)
  • Update github/codeql-action action to v3
  • Update kentaro-m/auto-assign-action action to v2
  • ๐Ÿ” Create all rate-limited PRs at once ๐Ÿ”

Open

These updates have all been created already. Click a checkbox below to force a retry/rebase of any.

Detected dependencies

github-actions
.github/workflows/assign-prs.yml
  • kentaro-m/auto-assign-action v1.2.5
.github/workflows/ci.yml
  • actions/checkout v3
  • aws-actions/configure-aws-credentials v2
  • actions/setup-dotnet v3
  • codecov/codecov-action v3
  • actions/upload-artifact v3
  • actions/upload-artifact v3
  • actions/upload-artifact v3
  • actions/checkout v3
  • actions/setup-dotnet v3
  • actions/download-artifact v3
  • actions/download-artifact v3
  • aws-actions/configure-aws-credentials v2
  • actions/checkout v3
  • aws-actions/configure-aws-credentials v2
  • actions/download-artifact v3
  • actions/download-artifact v3
  • actions/setup-dotnet v3
  • ncipollo/release-action v1
.github/workflows/codeql-analysis.yml
  • actions/checkout v3
  • actions/setup-dotnet v3
  • github/codeql-action v2
  • github/codeql-action v2
nuget
.config/dotnet-tools.json
  • nbgv 3.5.119
  • dotnet-format 7.0.360304
  • dotnet-json 1.1.1
Directory.Build.props
  • StyleCop.Analyzers 1.2.0-beta.333
  • Microsoft.SourceLink.GitHub 1.1.1
  • Nerdbank.GitVersioning 3.5.119
examples/AwsClientFactories/AwsClientFactories.csproj
  • System.ComponentModel.Annotations 5.0.0
  • AWSSDK.SecurityToken 3.7.0
  • AWSSDK.S3 3.7.0
examples/CustomConfiguration/CustomConfiguration.csproj
  • Microsoft.Extensions.Configuration.Json 7.0.0
examples/CustomResource/CustomResource.csproj
  • System.ComponentModel.Annotations 5.0.0
examples/CustomRuntime/CustomRuntime.csproj
  • AWSSDK.Core 3.7.0.31
examples/Directory.Build.props
  • Cythral.CloudFormation.BuildTasks 0.5.4
global.json
  • dotnet-sdk 7.0.202
src/Core/Core.csproj
  • Microsoft.Extensions.Options.ConfigurationExtensions 7.0.0
  • Microsoft.Extensions.Options 7.0.1
  • Microsoft.Extensions.Logging.Console 7.0.0
  • Microsoft.Extensions.Logging 7.0.0
  • Microsoft.Extensions.DependencyInjection 7.0.0
  • Microsoft.Extensions.Configuration.EnvironmentVariables 7.0.0
  • Microsoft.Extensions.Configuration 7.0.0
  • AWSSDK.Core 3.7.106.9
  • Amazon.Lambda.Core 2.1.0
src/CustomResource.BuildTime/CustomResource.BuildTime.csproj
  • System.ComponentModel.Annotations 5.0.0
  • Microsoft.CodeAnalysis.CSharp.Workspaces 4.5.0
  • Microsoft.CodeAnalysis 4.5.0
src/Encryption/Encryption.csproj
  • AWSSDK.KeyManagementService 3.7.101.77
src/Framework.BuildTime/Framework.BuildTime.csproj
  • Microsoft.CodeAnalysis 4.5.0
src/Generator/Generator.csproj
  • YamlDotNet 13.0.2
  • Microsoft.Bcl.AsyncInterfaces 7.0.0
  • Microsoft.Extensions.Hosting 7.0.1
  • System.Runtime.Loader 4.3.0
  • System.ComponentModel.Annotations 5.0.0
  • Microsoft.CodeAnalysis.CSharp.Workspaces 4.5.0
  • Microsoft.CodeAnalysis 4.5.0
src/Layer/Layer.csproj
  • Cythral.CloudFormation.BuildTasks 0.5.4
src/Runtime/Runtime.csproj
  • Amazon.Lambda.RuntimeSupport 1.8.6
src/Tracing/Tracing.csproj
  • AWSXRayRecorder.Handlers.AwsSdk 2.12.0
  • AWSXRayRecorder.Core 2.14.0
templates/Directory.Build.props
  • Microsoft.CodeAnalysis.CSharp.CodeStyle 4.5.0
  • Microsoft.CodeAnalysis 4.5.0
tests/Compilation/Projects/AmazonFactories/AmazonFactories.csproj
  • AWSSDK.SecurityToken 3.7.0
  • AWSSDK.S3 3.7.0
tests/Compilation/Projects/ConfigFactory/ConfigFactory.csproj
  • Microsoft.Extensions.Configuration.Json 7.0.0
tests/Compilation/Projects/Configuration/Configuration.csproj
  • NSubstitute 4.3.0
tests/Compilation/Projects/CustomResources/CustomResources.csproj
  • System.ComponentModel.Annotations 5.0.0
tests/Compilation/Projects/IamPermissions/IamPermissions.csproj
  • NSubstitute 4.2.2
  • AWSSDK.S3 3.7.0
tests/Compilation/Projects/Permissionless/Permissionless.csproj
  • NSubstitute 4.2.2
tests/Tests.csproj
  • YamlDotNet 13.0.2
  • McMaster.NETCore.Plugins 1.4.0
  • Microsoft.NET.Test.Sdk 17.5.0
  • NUnit3TestAdapter 4.4.2
  • nunit 3.13.3
  • NuGet.ProjectModel 6.5.0
  • NuGet.Commands 6.5.0
  • NuGet.Frameworks 6.5.0
  • NSubstitute 4.2.2
  • Microsoft.CodeAnalysis.Workspaces.MSBuild 4.5.0
  • Microsoft.Build.Locator 1.5.5
  • FluentAssertions 5.10.3
  • coverlet.collector 3.1.0
  • AutoFixture.NUnit3 4.17.0
  • AWSSDK.S3 3.7.1
  • AWSSDK.SecurityToken 3.7.1.18
  • AutoFixture.AutoNSubstitute 4.17.0

  • Check this box to trigger a request for Renovate to run again on this repository

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.