Skip to content
This repository has been archived by the owner on Mar 5, 2022. It is now read-only.

Commit

Permalink
Upgrade to .NET Core 3.1
Browse files Browse the repository at this point in the history
- Use built-in logging instead of Serilog
- Simplify configuration
- Use System.Json.Text instead of Newtonsoft.Json
  • Loading branch information
Gabriel Weyer committed May 31, 2020
1 parent 85b7f33 commit c3943e5
Show file tree
Hide file tree
Showing 17 changed files with 95 additions and 327 deletions.
31 changes: 17 additions & 14 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@

| CI | Status | Platform(s) | Framework(s) | Test Framework(s) |
| --- | --- | --- | --- | --- |
| [AppVeyor][app-veyor] | [![Build Status][app-veyor-shield]][app-veyor] | `Windows` | `nestandard2.0` | `netcoreapp2.2.0` |
| [AppVeyor][app-veyor] | [![Build Status][app-veyor-shield]][app-veyor] | `Windows` | `nestandard2.0` | `netcoreapp3.1` |

Allows to route a `SQS` message to a specific endpoint on the `Worker` instead of having a single endpoint handling all the messages.

Expand All @@ -22,12 +22,12 @@ StronglyTypedMessage model = new StronglyTypedMessage();

var sendMessageRequest = new SendMessageRequest
{
// Serialize your model as JSON
MessageBody = JsonConvert.SerializeObject(model)
// Set the QueueUrl
// Serialize your model as JSON (you can use Newtonsoft.Json if you prefer)
MessageBody = System.Text.Json.JsonSerializer.Serialize(model)
// Set the QueueUrl and other properties as you see fit
};

// AddRoutingAttribute is an extension method
// AddRoutingAttribute is an extension method in the "BeanstalkWorker.SimpleRouting" namespace
sendMessageRequest.MessageAttributes.AddRoutingAttribute("task-name");
```

Expand All @@ -37,19 +37,19 @@ A sample `Web` app is provided in [samples/SampleWeb](samples/SampleWeb).

You can send two distinct types of messages by hitting two different endpoints:

- `GET /send/work`
- `GET /send/nothing`
- `GET http://localhost:5000/send/work`
- `GET http://localhost:5000/send/nothing`

##### Configuration

Create a `iAM` user (if you don't have one already) which has access to `SQS`.

You'll need to configure four settings, either using [user secrets][secret-manager] (preferred way as they'll be shared with the sample worker), `appsettings.json` or via [environment variables][environment-variables]:
You'll need to configure four settings using [user secrets][secret-manager]:

- `Aws:RegionSystemName` - [region code][available-regions], for example `ap-southeast-2`
- `Aws:Queue:WorkerQueueUrl` - `URL` of the `SQS` queue, for example `https://sqs.ap-southeast-2.amazonaws.com/375985941080/dev-gabriel`
- `AWS_ACCESS_KEY_ID` - this is the `Access key ID` of your `iAM user`
- `AWS_SECRET_ACCESS_KEY` - this is the `Secret access key` of your `iAM user`
- `Aws:Queue:AccessKeyId` - this is the `Access key ID` of your `iAM user`
- `Aws:Queue:SecretAccessKey` - this is the `Secret access key` of your `iAM user`

### Worker Tier

Expand All @@ -60,9 +60,12 @@ In the `Configure` method of your `Startup` class:
```csharp
public void Configure(IApplicationBuilder app)
{
// The simple routing middleware needs to be added **before** configuring endpoint routing
app.UseHeaderRouting();

// Abbreviated for clarity
app.UseRouting();

app.UseEndpoints(endpoint => endpoint.MapControllers());
}
```

Expand All @@ -71,9 +74,10 @@ public void Configure(IApplicationBuilder app)
```csharp
// This is important, we do not want a prefix in front of the action's route
[Route("")]
public class SomeController : Controller
[ApiController]
public class SomeController : ControllerBase
{
// The route has to match the argument given to AddRoutingAttribute
// The route template has to match the argument given to AddRoutingAttribute
[HttpPost("task-name")]
public async Task<IActionResult> SomeMethod(StronglyTypedMessage model)
{
Expand Down Expand Up @@ -108,4 +112,3 @@ If you wish to run the `Worker` without deploying to `AWS Beanstalk` you can lev
[app-veyor]: https://ci.appveyor.com/project/GabrielWeyer/simple-routing
[app-veyor-shield]: https://ci.appveyor.com/api/projects/status/github/gabrielweyer/simple-routing?branch=master&svg=true
[secret-manager]: https://docs.microsoft.com/en-us/aspnet/core/security/app-secrets?view=aspnetcore-2.1&tabs=windows#secret-manager
[environment-variables]: https://docs.microsoft.com/en-us/aspnet/core/fundamentals/configuration/?view=aspnetcore-2.1#environment-variables-configuration-provider
11 changes: 5 additions & 6 deletions samples/Core/Core.csproj
Original file line number Diff line number Diff line change
@@ -1,15 +1,14 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netcoreapp2.1</TargetFramework>
<TargetFramework>netcoreapp3.1</TargetFramework>
<AssemblyName>BeanstalkWorker.SimpleRouting.Core</AssemblyName>
<RootNamespace>BeanstalkWorker.SimpleRouting.Core</RootNamespace>
<IsPackable>false</IsPackable>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="AWSSDK.SQS" Version="3.3.3.44" />
<PackageReference Include="BeanstalkWorker.SimpleRouting" Version="1.0.0" />
<PackageReference Include="Microsoft.Extensions.Logging.Abstractions" Version="2.2.0" />
<PackageReference Include="Microsoft.Extensions.Options" Version="2.2.0" />
<PackageReference Include="Newtonsoft.Json" Version="12.0.1" />
<PackageReference Include="AWSSDK.SQS" Version="3.3.102.118" />
<PackageReference Include="BeanstalkWorker.SimpleRouting" Version="1.0.2" />
<PackageReference Include="Microsoft.Extensions.Logging.Abstractions" Version="3.1.4" />
<PackageReference Include="Microsoft.Extensions.Options" Version="3.1.4" />
</ItemGroup>
</Project>
2 changes: 1 addition & 1 deletion samples/Core/Models/DoNothingMessage.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,6 @@ namespace BeanstalkWorker.SimpleRouting.Core.Models
{
public class DoNothingMessage
{
public TimeSpan RestDuration { get; set; }
public DateTimeOffset StartAt { get; set; }
}
}
2 changes: 1 addition & 1 deletion samples/Core/Models/DoWorkMessage.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,6 @@ namespace BeanstalkWorker.SimpleRouting.Core.Models
public class DoWorkMessage
{
public string NatureOfWork { get; set; }
public TimeSpan ExpectedDurationOfWork { get; set; }
public DateTimeOffset StartsAt { get; set; }
}
}
126 changes: 10 additions & 116 deletions samples/SampleWeb/Program.cs
Original file line number Diff line number Diff line change
@@ -1,129 +1,23 @@
using System;
using Amazon.SQS.Model;
using Microsoft.AspNetCore;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Configuration;
using Serilog;
using Serilog.Core;
using Serilog.Events;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Hosting;

namespace BeanstalkWorker.SimpleRouting.SampleWeb
{
public class Program
{
public static void Main(string[] args)
{
BuildWebHost(args).Run();
BuildWebHost(args).Build().Run();
}

private static IWebHost BuildWebHost(string[] args)
private static IHostBuilder BuildWebHost(string[] args)
{
var webHostBuilder = WebHost.CreateDefaultBuilder(args);

var contentRoot = webHostBuilder.GetSetting("contentRoot");
var environment = webHostBuilder.GetSetting("ENVIRONMENT");

var isDevelopment = EnvironmentName.Development.Equals(environment);

var configurationBuilder = new ConfigurationBuilder()
.SetBasePath(contentRoot)
.AddJsonFile("appsettings.json", false, false)
.AddEnvironmentVariables();

if (isDevelopment)
{
configurationBuilder.AddUserSecrets<Program>();
}

var configuration = configurationBuilder.Build();

var serilogLevel = configuration.GetLoggingLevel("MinimumLevel:Default");

var loggerConfiguration = new LoggerConfiguration()
.Destructure.ByTransforming<MessageAttributeValue>(a => new {a.DataType, a.StringValue})
.Enrich.WithDemystifiedStackTraces()
.ReadFrom.Configuration(configuration);

if (isDevelopment)
{
loggerConfiguration = loggerConfiguration.WriteTo.Console(serilogLevel);
}

var logger = loggerConfiguration.CreateLogger();

try
{
logger.Information("Starting Host...");

SetAwsEnvironmentVariables(configuration, logger);

return webHostBuilder
.UseStartup<Startup>()
.UseConfiguration(configuration)
.UseSerilog(logger, true)
.Build();
}
catch (Exception ex)
{
logger.Fatal(ex, "Host terminated unexpectedly");
throw;
}
}

private static void SetAwsEnvironmentVariables(IConfigurationRoot configuration, Logger logger)
{
const string awsAccessKeyIdVariableName = "AWS_ACCESS_KEY_ID";
const string awsSecretAccessKeyVariableName = "AWS_SECRET_ACCESS_KEY";

var accessKeyIdFromEnvironmentVariable = Environment.GetEnvironmentVariable(awsAccessKeyIdVariableName);
var secretAccessKeyFromEnvironmentVariable = Environment.GetEnvironmentVariable(awsSecretAccessKeyVariableName);

if (!string.IsNullOrWhiteSpace(accessKeyIdFromEnvironmentVariable) &&
!string.IsNullOrWhiteSpace(secretAccessKeyFromEnvironmentVariable))
{
logger.Information($"'{awsAccessKeyIdVariableName}' and '{awsSecretAccessKeyVariableName}' set via environment variables");

return;
}

var accessKeyIdFromConfiguration = configuration.GetValue<string>(awsAccessKeyIdVariableName);
var secretAccessKeyFromConfiguration = configuration.GetValue<string>(awsSecretAccessKeyVariableName);

if (!string.IsNullOrWhiteSpace(accessKeyIdFromConfiguration) &&
!string.IsNullOrWhiteSpace(secretAccessKeyFromConfiguration))
{
logger.Information($"'{awsAccessKeyIdVariableName}' and '{awsSecretAccessKeyVariableName}' present in configuration, setting matching environment variables");

Environment.SetEnvironmentVariable(
awsAccessKeyIdVariableName,
accessKeyIdFromConfiguration,
EnvironmentVariableTarget.Process);

Environment.SetEnvironmentVariable(
awsSecretAccessKeyVariableName,
secretAccessKeyFromConfiguration,
EnvironmentVariableTarget.Process);
}
else
{
throw new InvalidOperationException($"'{awsAccessKeyIdVariableName}' and '{awsSecretAccessKeyVariableName}' should either be set as environment variables or configuration, please refer to the README: https://github.com/gabrielweyer/simple-routing/blob/master/README.md#configuration.");
}
}
}

internal static class ConfigurationRootExtensions
{
internal static LogEventLevel GetLoggingLevel(this IConfigurationRoot configuration, string keyName,
LogEventLevel defaultLevel = LogEventLevel.Warning)
{
try
{
return configuration.GetValue($"Serilog:{keyName}", LogEventLevel.Warning);
}
catch (Exception)
{
return defaultLevel;
}
return Host
.CreateDefaultBuilder(args)
.ConfigureWebHostDefaults(webBuilder =>
{
webBuilder.UseStartup<Startup>();
});
}
}
}
11 changes: 2 additions & 9 deletions samples/SampleWeb/SampleWeb.csproj
Original file line number Diff line number Diff line change
@@ -1,19 +1,12 @@
<Project Sdk="Microsoft.NET.Sdk.Web">
<PropertyGroup>
<TargetFramework>netcoreapp2.2</TargetFramework>
<TargetFramework>netcoreapp3.1</TargetFramework>
<UserSecretsId>fa5b7428-c384-484b-bf75-1d9b2d1526a0</UserSecretsId>
<AssemblyName>BeanstalkWorker.SimpleRouting.SampleWeb</AssemblyName>
<RootNamespace>BeanstalkWorker.SimpleRouting.SampleWeb</RootNamespace>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="AWSSDK.SQS" Version="3.3.3.44" />
<PackageReference Include="BeanstalkWorker.SimpleRouting" Version="1.0.0" />
<PackageReference Include="Microsoft.AspNetCore.All" />
<PackageReference Include="Serilog" Version="2.7.1" />
<PackageReference Include="Serilog.AspNetCore" Version="2.1.1" />
<PackageReference Include="Serilog.Enrichers.Demystify" Version="0.1.0-dev-00016" />
<PackageReference Include="Serilog.Settings.Configuration" Version="3.0.1" />
<PackageReference Include="Serilog.Sinks.Console" Version="3.1.1" />
<PackageReference Include="AWSSDK.SQS" Version="3.3.102.118" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\Core\Core.csproj" />
Expand Down
13 changes: 8 additions & 5 deletions samples/SampleWeb/SendController.cs
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
using System;
using System.Threading.Tasks;
using BeanstalkWorker.SimpleRouting.Core.Logic;
using BeanstalkWorker.SimpleRouting.Core.Models;
using Microsoft.AspNetCore.Mvc;

namespace BeanstalkWorker.SimpleRouting.SampleWeb
{
public class SendController : Controller
[ApiController]
[Route("send")]
public class SendController : ControllerBase
{
private readonly ISqsClient _sqsClient;

Expand All @@ -15,22 +16,24 @@ public SendController(ISqsClient sqsClient)
_sqsClient = sqsClient;
}

[Route("work")]
public async Task Work()
{
var body = new DoWorkMessage
{
ExpectedDurationOfWork = TimeSpan.FromSeconds(3),
NatureOfWork = "Not much"
NatureOfWork = "Not much",
StartsAt = DateTimeOffset.UtcNow.AddYears(3)
};

await _sqsClient.SendDoWorkMessageAsync(body);
}

[Route("nothing")]
public async Task Nothing()
{
var body = new DoNothingMessage
{
RestDuration = TimeSpan.FromDays(3)
StartAt = DateTimeOffset.UtcNow.AddSeconds(1)
};

await _sqsClient.SendDoNothingMessageAsync(body);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,8 @@
using BeanstalkWorker.SimpleRouting.Core.Options;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using Newtonsoft.Json;

namespace BeanstalkWorker.SimpleRouting.Core.Logic
namespace BeanstalkWorker.SimpleRouting.SampleWeb
{
public class SqsClient : ISqsClient
{
Expand Down Expand Up @@ -49,7 +48,7 @@ SendMessageRequest GenerateRequest()
{
var sendMessageRequest = new SendMessageRequest
{
MessageBody = JsonConvert.SerializeObject(body),
MessageBody = System.Text.Json.JsonSerializer.Serialize(body),
QueueUrl = _queueOptions.WorkerQueueUrl.ToString()
};

Expand Down
25 changes: 10 additions & 15 deletions samples/SampleWeb/Startup.cs
Original file line number Diff line number Diff line change
@@ -1,10 +1,7 @@
using Amazon;
using Amazon.SQS;
using BeanstalkWorker.SimpleRouting.Core.Logic;
using BeanstalkWorker.SimpleRouting.Core.Options;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;

Expand All @@ -25,21 +22,16 @@ public void ConfigureServices(IServiceCollection services)

ConfigureOptions(services);

services.AddOptions();
services
.AddMvc()
.SetCompatibilityVersion(CompatibilityVersion.Version_2_2);
.AddOptions()
.AddControllers();
}

public void Configure(IApplicationBuilder app, IHostingEnvironment env)
public void Configure(IApplicationBuilder app)
{
app.UseMvc(routes =>
{
routes.MapRoute(
name: "default",
template: "{controller}/{action}/{id?}",
defaults: new {controller = "Send", action = "Nothing"});
});
app.UseRouting();

app.UseEndpoints(endpoint => endpoint.MapControllers());
}

private void ConfigureOptions(IServiceCollection services)
Expand All @@ -54,7 +46,10 @@ private void AddServices(IServiceCollection services)
var systemName = _configuration.GetValue<string>("Aws:RegionSystemName");
var regionEndpoint = RegionEndpoint.GetBySystemName(systemName);
return new AmazonSQSClient(regionEndpoint);
var accessKeyId = _configuration.GetValue<string>("Aws:Queue:AccessKeyId");
var secretAccessKey = _configuration.GetValue<string>("Aws:Queue:SecretAccessKey");
return new AmazonSQSClient(accessKeyId, secretAccessKey, regionEndpoint);
});
services.AddScoped<ISqsClient, SqsClient>();
}
Expand Down
Loading

0 comments on commit c3943e5

Please sign in to comment.