Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Cloud events + publishing to Azure Service Bus #143

Open
wants to merge 8 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
84 changes: 65 additions & 19 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,8 @@ A simulator that provides endpoints to mimic the functionality of [Azure Event G

Topics and their subscribers are configured in the `appsettings.json` file.

You can add multiple topics. Each topic must have a unique port. Each topic can have multiple subscribers.
An example of one topic with one subscriber is shown below.
You can add multiple topics. Each topic must have a unique port. Each topic can have multiple subscribers. Each topic can support EventGridEvents or CloudEvent schemas.
An example of one topic with support for an EventGridEvent schema and with one HTTP endpoint subscriber is shown below.

```json
{
Expand All @@ -22,13 +22,16 @@ An example of one topic with one subscriber is shown below.
"name": "MyAwesomeTopic",
"port": 60101,
"key": "TheLocal+DevelopmentKey=",
"subscribers": [
{
"name": "LocalAzureFunctionSubscription",
"endpoint": "http://localhost:7071/runtime/webhooks/EventGrid?functionName=PersistEventToDb",
"disableValidation": true
}
]
"type": "EventGridEvent",
"subscribers": {
"http": [
{
"name": "LocalAzureFunctionSubscription",
"endpoint": "http://localhost:7071/runtime/webhooks/EventGrid?functionName=PersistEventToDb",
"disableValidation": true
}
]
}
}
]
}
Expand All @@ -39,24 +42,66 @@ An example of one topic with one subscriber is shown below.
- `name`: The name of the topic. It can only contain letters, numbers, and dashes.
- `port`: The port to use for the topic endpoint. The topic will listen on `https://0.0.0.0:{port}/`.
- `key`: The key that will be used to validate the `aeg-sas-key` or `aeg-sas-token` header in each request. If this is not supplied then no key validation will take place.
- `type`: The event schema for the topic. Both EventGridEvent (https://learn.microsoft.com/en-us/azure/event-grid/event-schema) and CloudSchema (https://learn.microsoft.com/en-us/azure/event-grid/cloud-event-schema) schemas are supported.
- `subscribers`: The subscriptions for this topic.

### Subscriber Settings

Two subscriber classifications are supported - HTTP endpoints and Azure Service Bus.

#### HTTP

- `name`: The name of the subscriber. It can only contain letters, numbers, and dashes.
- `endpoint`: The subscription endpoint url. Events received by topic will be sent to this address.
- `disableValidation`:
- `false` (the default) subscription validation will be attempted each time the simulator starts.
- `true` to disable subscription validation.

#### Subscription Validation
##### 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.

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 will mimick this validation behaviour at start up but it can be disabled using the `disableValidation` setting (above).

#### Azure Service Bus

- `name`: The name of the subscriber. It can only contain letters, numbers, and dashes.
- `sharedAccessKeyName`: The shared access policy name.
- `sharedAccessKey`: The shared access policy key.
- `topic`: Destination topic/queue name.
- `disableValidation`:
- `false` (the default) subscription validation will be attempted each time the simulator starts.
- `true` to disable subscription validation.
- `properties`: Define headers that are included with the request sent to the destination.

##### Delivery Properties

Define headers that are included with the request sent to the destination.

- element: User property name.
- `type`: `dynamic` or `static`.
- `value`: JsonPath property selection.

Extending the example above to include two user properties (`message` and `eTag`). `message` will always have the value of `hello world` and `eTag` will use the value of the `eTag` property in the event's data payload.

```json
{
"properties":
{
"message": {
"type": "static",
"value" : "Hello world"
},
"eTag": {
"type": "dynamic",
"value": "data.eTag"
}
}
}
```

#### 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.
Expand All @@ -71,15 +116,17 @@ Extending the example above to include a basic filter which will only deliver ev
"name": "MyAwesomeTopic",
"port": 60101,
"key": "TheLocal+DevelopmentKey=",
"subscribers": [
{
"name": "LocalAzureFunctionSubscription",
"endpoint": "http://localhost:7071/runtime/webhooks/EventGrid?functionName=PersistEventToDb",
"filter": {
"includedEventTypes": ["my.eventType"]
"subscribers":
"http": [
{
"name": "LocalAzureFunctionSubscription",
"endpoint": "http://localhost:7071/runtime/webhooks/EventGrid?functionName=PersistEventToDb",
"filter": {
"includedEventTypes": ["my.eventType"]
}
}
}
]
]
}
}
]
}
Expand Down Expand Up @@ -262,7 +309,6 @@ It posts the payload to https://host:port and drops the query uri. All of the ex

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

- `CloudEvent` schema support.
- Subscriber retries & dead lettering. https://docs.microsoft.com/en-us/azure/event-grid/delivery-and-retry
- Certificate configuration in `appsettings.json`.
- Subscriber token auth
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
using System;
using System.Net;
using System.Threading.Tasks;
using Azure;
using Azure.Core;
Expand All @@ -10,9 +9,7 @@
namespace AzureEventGridSimulator.Tests.ActualSimulatorTests;

/// <summary>
/// Simple tests to check that we can send an event via Azure.Messaging.EventGrid library.
/// NOTE: These tests require (and automatically start) an actual instance of AzureEventGridSimulator.exe as there is no way to inject an HttpClient (from a WebApplicationFactory)
/// into Azure.Messaging.EventGrid.
/// NOTE: These tests require (and automatically start) an actual instance of AzureEventGridSimulator.exe as WebApplicationFactory does not use a TCP connection (no port in use).
/// </summary>
[Collection(nameof(ActualSimulatorFixtureCollection))]
[Trait("Category", "integration-actual")]
Expand All @@ -26,40 +23,6 @@ public AzureMessagingEventGridTest(ActualSimulatorFixture actualSimulatorFixture
_actualSimulatorFixture = actualSimulatorFixture;
}

[Fact]
public async Task GivenValidEvent_WhenUriContainsNonStandardPort_ThenItShouldBeAccepted()
{
var client = new EventGridPublisherClient(
new Uri("https://localhost:60101/api/events"),
new AzureKeyCredential("TheLocal+DevelopmentKey="),
new EventGridPublisherClientOptions
{ Retry = { Mode = RetryMode.Fixed, MaxRetries = 0, NetworkTimeout = TimeSpan.FromSeconds(5) } });

var response = await client.SendEventAsync(new EventGridEvent("/the/subject", "The.Event.Type", "v1", new { Id = 1, Foo = "Bar" }));

response.Status.ShouldBe((int)HttpStatusCode.OK);
}

[Fact]
public async Task GivenValidEvents_WhenUriContainsNonStandardPort_TheyShouldBeAccepted()
{
var client = new EventGridPublisherClient(
new Uri("https://localhost:60101/api/events"),
new AzureKeyCredential("TheLocal+DevelopmentKey="),
new EventGridPublisherClientOptions
{ Retry = { Mode = RetryMode.Fixed, MaxRetries = 0, NetworkTimeout = TimeSpan.FromSeconds(5) } });

var events = new[]
{
new EventGridEvent("/the/subject1", "The.Event.Type1", "v1", new { Id = 1, Foo = "Bar" }),
new EventGridEvent("/the/subject2", "The.Event.Type2", "v1", new { Id = 2, Foo = "Baz" })
};

var response = await client.SendEventsAsync(events);

response.Status.ShouldBe((int)HttpStatusCode.OK);
}

[Fact]
public async Task GivenValidEvent_WhenUriContainsNonExistentPort_ThenItShouldNotBeAccepted()
{
Expand All @@ -78,22 +41,4 @@ await client.SendEventAsync(new EventGridEvent("/the/subject", "The.Event.Type",
exception.Message.ShouldContain("refused");
exception.Status.ShouldBe(0);
}

[Fact]
public async Task GivenValidEvent_WhenKeyIsWrong_ThenItShouldNotBeAccepted()
{
var client = new EventGridPublisherClient(
new Uri("https://localhost:60101/api/events"),
new AzureKeyCredential("TheWrongLocal+DevelopmentKey="),
new EventGridPublisherClientOptions
{ Retry = { Mode = RetryMode.Fixed, MaxRetries = 0, NetworkTimeout = TimeSpan.FromSeconds(5) } });

var exception = await Should.ThrowAsync<RequestFailedException>(async () =>
{
await client.SendEventAsync(new EventGridEvent("/the/subject", "The.Event.Type", "v1",
new { Id = 1, Foo = "Bar" }));
});

exception.Status.ShouldBe((int)HttpStatusCode.Unauthorized);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
<PackageReference Include="Microsoft.AspNetCore.Mvc.Testing" Version="7.0.2" />
<PackageReference Include="Newtonsoft.Json" Version="13.0.2" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.4.1" />
<PackageReference Include="RichardSzalay.MockHttp" Version="6.0.0" />
<PackageReference Include="Shouldly" Version="4.1.0" />
<PackageReference Include="xunit" Version="2.4.2" />
<PackageReference Include="xunit.runner.visualstudio" Version="2.4.5">
Expand Down
71 changes: 0 additions & 71 deletions src/AzureEventGridSimulator.Tests/IntegrationTests/BasicTests.cs

This file was deleted.

Loading