Skip to content

Commit

Permalink
Dotnet 3 1 migration (#18)
Browse files Browse the repository at this point in the history
* Migrate to dotnet 3.1.
* Update nugets
* Add additional aeg headers.
* Updated README
* Logging tweaks
* Added example settings file with serilog configuration
* Updated the postman collection
  • Loading branch information
pm7y authored Nov 17, 2020
1 parent c3891c6 commit 4f83690
Show file tree
Hide file tree
Showing 33 changed files with 559 additions and 422 deletions.
9 changes: 9 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -328,6 +328,8 @@ ASALocalRun/

# MFractors (Xamarin productivity tool) working folder
.mfractor/

# Build artifacts
/win-x64
/src/AzureEventGridSimulator/appsettings.development.json
/build-win64
Expand All @@ -336,3 +338,10 @@ ASALocalRun/
/src/build-win-64
/src/build-osx-64
/src/build-linux-64
.vscode/
build-win-64
build-osx-64
build-linux-64

# vscode
src/AzureEventGridSimulator/.config/
23 changes: 11 additions & 12 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -51,18 +51,15 @@ An example of one topic with one subscriber is shown below.

#### Subscription Validation

When a subscription is added to Azure Event Grid it first sends a validation event to the subscription endpoint. The validation event contains a `validationCode` which the subscription endpoint must echo back. If this does not occur then Azure Event Grid will not enable the subscription. Azure Event Grid also supports manual validation via a `validationUrl` which is sent with the `validationCode` in the initial validation message.
When a subscription is added to Azure Event Grid it first sends a validation event to the subscription endpoint. The validation event contains a `validationCode` which the subscription endpoint must echo back. If this does not occur then Azure Event Grid will not enable the subscription.

More information about subscription validation can be found at [https://docs.microsoft.com/en-us/azure/event-grid/security-authentication](https://docs.microsoft.com/en-us/azure/event-grid/security-authentication).
More information about subscription validation can be found at [https://docs.microsoft.com/en-us/azure/event-grid/webhook-event-delivery](https://docs.microsoft.com/en-us/azure/event-grid/webhook-event-delivery).

The Azure Event Grid Simualator can mimick this behaviour using the `validationRequired` setting.

- `false` (the default), subscription validation will be disabled.
- `true`, a subscription validation event will be sent to the subscriber when the simulator starts. The subscription will not accept events until it is successfully validated.
The Azure Event Grid Simualator will mimick this validation behaviour at start up but it can be disabled using the `disableValidation` setting (above).

#### Filtering Events

Event filtering is configurable on each subscriber using the filter model defined here: https://docs.microsoft.com/en-us/azure/event-grid/event-filtering. This page provides a full guide to the configuration options available and all parts of this guide are currently supported. For ease of transition, explicit limitations have also been adhered to.
Event filtering is configurable on each subscriber using the filter model defined here: https://docs.microsoft.com/en-us/azure/event-grid/event-filtering. This page provides a full guide to the configuration options available and all parts of this guide are currently supported. For ease of transition, explicit limitations have also been adhered to.
The restrictions mentioned have been further modified (https://azure.microsoft.com/en-us/updates/advanced-filtering-generally-available-in-event-grid/) and these new less restrictive filtering limits have been observed.

Extending the example above to include a basic filter which will only deliver events to the subscription if they are of a specific type is illustrated below.
Expand All @@ -72,7 +69,7 @@ Extending the example above to include a basic filter which will only deliver ev
"topics": [
{
"name": "MyAwesomeTopic",
"httpsPort": 60101,
"port": 60101,
"key": "TheLocal+DevelopmentKey=",
"subscribers": [
{
Expand Down Expand Up @@ -117,7 +114,6 @@ or advanced filtering:
}
```


## Using the Simulator

Once configured and running, requests are `posted` to a topic endpoint. The endpoint of a topic will be in the form: `https://localhost:<configured-port>/api/events?api-version=2018-01-01`.
Expand Down Expand Up @@ -208,7 +204,10 @@ It posts the payload to https://host:port and drops the query uri. All of the ex

## Future Development

Some features that could be added if there was a need for them: -

- Subscriber retries & dead lettering. https://docs.microsoft.com/en-us/azure/event-grid/delivery-and-retry
- Ceritifcate cofiguration in `appsettings.json`.
- Docker support.
- Maybe a web based console?
- Certificate configuration in `appsettings.json`.
- Subscriber token auth
- Better Docker support.
- Maybe a web based console for admin stats etc.
18 changes: 11 additions & 7 deletions src/Azure Event Grid Simulator.postman_collection.json
Original file line number Diff line number Diff line change
Expand Up @@ -44,18 +44,21 @@
],
"body": {
"mode": "raw",
"raw": "[{\n \"id\": \"{{eventId}}\",\n \"subject\": \"/example/subject\",\n \"data\": {\n \t\"MyProperty\": \"This is my awesome data!\"\n },\n \"eventType\": \"some.special.event.type\",\n \"eventTime\": \"{{eventTime}}\",\n \"dataVersion\": \"1\",\n}]"
"raw": "[{\n \"id\": \"{{eventId}}\",\n \"subject\": \"/example/subject\",\n \"data\": {\n \t\"MyProperty\": \"This is my awesome data!\"\n },\n \"eventType\": \"some.special.event.type\",\n \"eventTime\": \"{{eventTime}}\",\n \"dataVersion\": \"1\"\n}]",
"options": {
"raw": {}
}
},
"url": {
"raw": "https://127.0.0.1:60101/api/events?api-version=2018-01-01",
"raw": "https://127.0.0.1:60102/api/events?api-version=2018-01-01",
"protocol": "https",
"host": [
"127",
"0",
"0",
"1"
],
"port": "60101",
"port": "60102",
"path": [
"api",
"events"
Expand All @@ -71,20 +74,20 @@
"response": []
},
{
"name": "Subscription Validation",
"name": "Manual Validation",
"request": {
"method": "GET",
"header": [],
"url": {
"raw": "https://127.0.0.1:60101/validate?id=d9ebebb7-f884-40b5-8c0d-9ec72f6f19cd",
"raw": "https://127.0.0.1:60102/validate?id=d9ebebb7-f884-40b5-8c0d-9ec72f6f19cd",
"protocol": "https",
"host": [
"127",
"0",
"0",
"1"
],
"port": "60101",
"port": "60102",
"path": [
"validate"
],
Expand All @@ -98,5 +101,6 @@
},
"response": []
}
]
],
"protocolProfileBehavior": {}
}
14 changes: 9 additions & 5 deletions src/AzureEventGridSimulator/AzureEventGridSimulator.csproj
Original file line number Diff line number Diff line change
@@ -1,15 +1,19 @@
<Project Sdk="Microsoft.NET.Sdk.Web">

<PropertyGroup>
<TargetFramework>netcoreapp2.2</TargetFramework>
<TargetFramework>netcoreapp3.1</TargetFramework>
<TargetLatestRuntimePatch>true</TargetLatestRuntimePatch>
</PropertyGroup>

<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|AnyCPU'">
<DebugType>none</DebugType>
<DebugSymbols>false</DebugSymbols>
</PropertyGroup>

<ItemGroup>
<PackageReference Include="MediatR" Version="8.0.1" />
<PackageReference Include="MediatR.Extensions.Microsoft.DependencyInjection" Version="8.0.0" />
<PackageReference Include="Microsoft.AspNetCore.App" />
<PackageReference Include="Serilog.AspNetCore" Version="3.2.0" />
<PackageReference Include="MediatR" Version="9.0.0" />
<PackageReference Include="MediatR.Extensions.Microsoft.DependencyInjection" Version="9.0.0" />
<PackageReference Include="Serilog.AspNetCore" Version="3.4.0" />
<PackageReference Include="Serilog.Enrichers.Environment" Version="2.1.3" />
<PackageReference Include="Serilog.Extensions.Logging" Version="3.0.1" />
<PackageReference Include="Serilog.Settings.Configuration" Version="3.1.0" />
Expand Down
34 changes: 29 additions & 5 deletions src/AzureEventGridSimulator/Controllers/NotificationController.cs
Original file line number Diff line number Diff line change
@@ -1,28 +1,52 @@
using System.Threading.Tasks;
using System.Linq;
using System.Net;
using System.Threading.Tasks;
using AzureEventGridSimulator.Domain.Commands;
using AzureEventGridSimulator.Domain.Entities;
using AzureEventGridSimulator.Infrastructure;
using AzureEventGridSimulator.Infrastructure.Extensions;
using AzureEventGridSimulator.Infrastructure.Settings;
using MediatR;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Http.Extensions;
using Microsoft.AspNetCore.Mvc;
using Newtonsoft.Json;

namespace AzureEventGridSimulator.Controllers
{
[Route("/api/events")]
[ApiController]
public class NotificationController : SimulatorController
public class NotificationController : ControllerBase
{
private const string SUPPORTED_API_VERSION = "2018-01-01";

private readonly IMediator _mediator;
private readonly SimulatorSettings _simulatorSettings;

public NotificationController(SimulatorSettings simulatorSettings,
IMediator mediator) : base(simulatorSettings)
IMediator mediator)
{
_mediator = mediator;
_simulatorSettings = simulatorSettings;
}

[HttpPost]
public async Task<IActionResult> Post()
public async Task<IActionResult> Post([FromQuery(Name = "api-version")] string apiVersion)
{
await _mediator.Send(new SendNotificationEventsToSubscriberCommand(Events, Topic));
if (!string.IsNullOrWhiteSpace(apiVersion) && !string.Equals(SUPPORTED_API_VERSION, apiVersion))
{
return BadRequest(new ErrorMessage(
HttpStatusCode.BadRequest,
$"The HTTP resource that matches the request URI '{HttpContext.Request.GetDisplayUrl()}' does not support the API version '{apiVersion}'.",
"UnsupportedApiVersion"));
}

var topicSettingsForCurrentRequestPort = _simulatorSettings.Topics.First(t => t.Port == HttpContext.Connection.LocalPort);
var eventsFromCurrentRequestBody = JsonConvert.DeserializeObject<EventGridEvent[]>(HttpContext.RequestBody().Result);

await _mediator.Send(new SendNotificationEventsToSubscriberCommand(eventsFromCurrentRequestBody, topicSettingsForCurrentRequestPort));

Response.Headers.Add("api-supported-versions", SUPPORTED_API_VERSION);
return Ok();
}
}
Expand Down
40 changes: 0 additions & 40 deletions src/AzureEventGridSimulator/Controllers/SimulatorController.cs

This file was deleted.

Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using System;
using System.Linq;
using System.Net;
using System.Threading.Tasks;
using AzureEventGridSimulator.Domain.Commands;
Expand All @@ -11,24 +12,27 @@ namespace AzureEventGridSimulator.Controllers
{
[Route("/validate")]
[ApiController]
public class SubscriptionValidationController : SimulatorController
public class SubscriptionValidationController : ControllerBase
{
private readonly SimulatorSettings _simulatorSettings;
private readonly IMediator _mediator;

public SubscriptionValidationController(SimulatorSettings simulatorSettings,
IMediator mediator) : base(simulatorSettings)
IMediator mediator)
{
_simulatorSettings = simulatorSettings;
_mediator = mediator;
}

[HttpGet]
public async Task<IActionResult> Get(Guid id)
{
var isValid = await _mediator.Send(new ValidateSubscriptionCommand(Topic, id));
var topicSettingsForCurrentRequestPort = _simulatorSettings.Topics.First(t => t.Port == HttpContext.Connection.LocalPort);
var isValid = await _mediator.Send(new ValidateSubscriptionCommand(topicSettingsForCurrentRequestPort, id));

if (!isValid)
{
return BadRequest(new ErrorMessage(HttpStatusCode.BadRequest, "The validation code was not correct."));
return BadRequest(new ErrorMessage(HttpStatusCode.BadRequest, "The validation code was not correct.", null));
}

return Ok("Webhook successfully validated as a subscription endpoint");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,12 +12,13 @@

namespace AzureEventGridSimulator.Domain.Commands
{
// ReSharper disable once UnusedMember.Global
public class SendNotificationEventsToSubscriberCommandHandler : AsyncRequestHandler<SendNotificationEventsToSubscriberCommand>
{
private readonly IHttpClientFactory _httpClientFactory;
private readonly ILogger _logger;
private readonly ILogger<SendNotificationEventsToSubscriberCommandHandler> _logger;

public SendNotificationEventsToSubscriberCommandHandler(IHttpClientFactory httpClientFactory, ILogger logger)
public SendNotificationEventsToSubscriberCommandHandler(IHttpClientFactory httpClientFactory, ILogger<SendNotificationEventsToSubscriberCommandHandler> logger)
{
_httpClientFactory = httpClientFactory;
_logger = logger;
Expand Down Expand Up @@ -47,6 +48,12 @@ private async Task SendToSubscriber(SubscriptionSettings subscription, EventGrid
{
try
{
if (subscription.Disabled)
{
_logger.LogWarning("Subscription '{SubscriberName}' is disabled and so Notification was skipped.", subscription.Name);
return;
}

if (!subscription.DisableValidation &&
subscription.ValidationStatus != SubscriptionValidationStatus.ValidationSuccessful)
{
Expand All @@ -62,16 +69,19 @@ private async Task SendToSubscriber(SubscriptionSettings subscription, EventGrid
{
if (subscription.Filter.AcceptsEvent(evt))
{
// ReSharper disable once MethodHasAsyncOverload
var json = JsonConvert.SerializeObject(new[] { evt }, Formatting.Indented);
using (var content = new StringContent(json, Encoding.UTF8, "application/json"))
{
var httpClient = _httpClientFactory.CreateClient();
httpClient.DefaultRequestHeaders.Add("aeg-event-type", "Notification");
httpClient.Timeout = TimeSpan.FromSeconds(15);
using var content = new StringContent(json, Encoding.UTF8, "application/json");
var httpClient = _httpClientFactory.CreateClient();
httpClient.DefaultRequestHeaders.Add("aeg-event-type", "Notification");
httpClient.DefaultRequestHeaders.Add("aeg-subscription-name", subscription.Name.ToUpperInvariant());
httpClient.DefaultRequestHeaders.Add("aeg-data-version", evt.DataVersion);
httpClient.DefaultRequestHeaders.Add("aeg-metadata-version", evt.MetadataVersion);
httpClient.DefaultRequestHeaders.Add("aeg-delivery-count", "0"); // TODO implement re-tries
httpClient.Timeout = TimeSpan.FromSeconds(60);

await httpClient.PostAsync(subscription.Endpoint, content)
.ContinueWith(t => LogResult(t, evt, subscription));
}
await httpClient.PostAsync(subscription.Endpoint, content)
.ContinueWith(t => LogResult(t, evt, subscription));
}
else
{
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
using MediatR;

namespace AzureEventGridSimulator.Domain.Commands
{
public class ValidateAllSubscriptionsCommand : IRequest
{

}
}
Loading

0 comments on commit 4f83690

Please sign in to comment.