Skip to content

Commit 0ac14ed

Browse files
authored
Support service bus subscribers (#208)
* Support service bus subscribers
1 parent 5aecd6d commit 0ac14ed

27 files changed

Lines changed: 2277 additions & 93 deletions

Dockerfile

Lines changed: 16 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -2,26 +2,33 @@
22
FROM --platform=$BUILDPLATFORM mcr.microsoft.com/dotnet/sdk:10.0-alpine as build
33
WORKDIR /source
44

5+
# copy build configuration files first
6+
COPY /src/Directory.Build.props .
7+
COPY /src/Directory.Packages.props .
8+
59
# copy source
6-
COPY /src/AzureEventGridSimulator .
10+
COPY /src/AzureEventGridSimulator ./AzureEventGridSimulator
711

812
ARG TARGETARCH
9-
RUN arch=$TARGETARCH \
10-
&& if [ "$TARGETARCH" = "amd64" ]; then arch="x64"; fi \
11-
&& echo $arch > /tmp/arch
12-
13+
RUN rid="linux-musl-arm64" \
14+
&& if [ "$TARGETARCH" = "amd64" ]; then rid="linux-musl-x64"; fi \
15+
&& echo $rid > /tmp/rid
16+
1317
# build source and publish as single file called 'AzureEventGridSimulator'
14-
RUN dotnet publish -c release -o /artifact \
15-
-r alpine-$(cat /tmp/arch) \
18+
# Note: DesignTimeBuild=true skips the CSharpier formatting target
19+
# Note: Trimming is disabled because MediatR and Asp.Versioning use reflection-based DI
20+
RUN dotnet publish ./AzureEventGridSimulator/AzureEventGridSimulator.csproj \
21+
-c release -o /artifact \
22+
-r $(cat /tmp/rid) \
1623
-f net10.0 \
1724
-v q \
1825
--nologo \
1926
--self-contained true \
2027
-p:PublishReadyToRun=false \
2128
-p:IncludeNativeLibrariesForSelfExtract=true \
2229
-p:PublishSingleFile=true \
23-
-p:PublishTrimmed=true \
24-
-p:TrimUnusedDependencies=true
30+
-p:PublishTrimmed=false \
31+
-p:DesignTimeBuild=true
2532

2633
# add binary artifact to new runtime-deps only image
2734
FROM --platform=$TARGETPLATFORM mcr.microsoft.com/dotnet/runtime-deps:10.0-alpine

README.md

Lines changed: 135 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -47,13 +47,134 @@ An example of one topic with one subscriber is shown below.
4747

4848
### Subscriber Settings
4949

50+
The simulator supports two types of subscribers: **HTTP webhooks** and **Azure Service Bus** (queues and topics).
51+
52+
#### Grouped Format (Recommended)
53+
54+
```json
55+
{
56+
"subscribers": {
57+
"http": [
58+
{
59+
"name": "WebhookSubscription",
60+
"endpoint": "https://example.com/webhook"
61+
}
62+
],
63+
"serviceBus": [
64+
{
65+
"name": "ServiceBusSubscription",
66+
"connectionString": "Endpoint=sb://my-namespace.servicebus.windows.net/;SharedAccessKeyName=...;SharedAccessKey=...",
67+
"queue": "my-queue"
68+
}
69+
]
70+
}
71+
}
72+
```
73+
74+
#### Legacy Format (Still Supported)
75+
76+
For backwards compatibility, a flat array of HTTP subscribers is still supported:
77+
78+
```json
79+
{
80+
"subscribers": [
81+
{
82+
"name": "WebhookSubscription",
83+
"endpoint": "https://example.com/webhook"
84+
}
85+
]
86+
}
87+
```
88+
89+
#### HTTP Subscriber Settings
90+
5091
| Setting | Description |
5192
|---------|-------------|
5293
| `name` | The name of the subscriber. It can only contain letters, numbers, and dashes. |
5394
| `endpoint` | The subscription endpoint url. Events received by topic will be sent to this address. |
5495
| `disableValidation` | Set to `true` to disable subscription validation. Default is `false`, which means subscription validation will be attempted each time the simulator starts. |
5596
| `deliverySchema` | (Optional) Override the delivery schema for this specific subscriber. Values: `EventGridSchema` or `CloudEventV1_0`. Takes precedence over the topic's `outputSchema`. |
5697

98+
#### Service Bus Subscriber Settings
99+
100+
| Setting | Description |
101+
|---------|-------------|
102+
| `name` | The name of the subscriber. |
103+
| `connectionString` | The Service Bus connection string. Either this OR (`namespace` + `sharedAccessKeyName` + `sharedAccessKey`) must be provided. |
104+
| `namespace` | The Service Bus namespace (without `.servicebus.windows.net` suffix). |
105+
| `sharedAccessKeyName` | The shared access key name (e.g., `RootManageSharedAccessKey`). |
106+
| `sharedAccessKey` | The shared access key. |
107+
| `queue` | The queue name. Either `queue` or `topic` must be specified (not both). |
108+
| `topic` | The topic name. Either `queue` or `topic` must be specified (not both). |
109+
| `deliverySchema` | (Optional) Override the delivery schema. Values: `EventGridSchema` or `CloudEventV1_0`. |
110+
| `properties` | (Optional) Custom delivery properties to add to Service Bus messages. See below. |
111+
112+
#### Service Bus Delivery Properties
113+
114+
You can add custom application properties to Service Bus messages using static or dynamic values:
115+
116+
```json
117+
{
118+
"properties": {
119+
"Region": {
120+
"type": "static",
121+
"value": "west-us"
122+
},
123+
"EventType": {
124+
"type": "dynamic",
125+
"value": "EventType"
126+
},
127+
"CustomerId": {
128+
"type": "dynamic",
129+
"value": "data.customerId"
130+
}
131+
}
132+
}
133+
```
134+
135+
- **Static properties**: The `value` is used as-is.
136+
- **Dynamic properties**: The `value` is a path to extract from the event. Supported paths:
137+
- Top-level fields: `Id`, `Subject`, `EventType`, `EventTime`, `DataVersion`, `Source`/`Topic`
138+
- Data properties: `data.propertyName` or `data.nested.property`
139+
140+
#### Complete Example
141+
142+
```json
143+
{
144+
"topics": [
145+
{
146+
"name": "OrdersTopic",
147+
"port": 60101,
148+
"key": "TheLocal+DevelopmentKey=",
149+
"subscribers": {
150+
"http": [
151+
{
152+
"name": "WebhookSubscription",
153+
"endpoint": "https://myapp.com/webhooks/orders",
154+
"disableValidation": true,
155+
"filter": {
156+
"includedEventTypes": ["Order.Created", "Order.Updated"]
157+
}
158+
}
159+
],
160+
"serviceBus": [
161+
{
162+
"name": "OrdersQueueSubscription",
163+
"connectionString": "Endpoint=sb://my-namespace.servicebus.windows.net/;SharedAccessKeyName=RootManageSharedAccessKey;SharedAccessKey=...",
164+
"queue": "orders-queue",
165+
"properties": {
166+
"OrderId": { "type": "dynamic", "value": "data.orderId" },
167+
"EventType": { "type": "dynamic", "value": "EventType" },
168+
"Source": { "type": "static", "value": "EventGridSimulator" }
169+
}
170+
}
171+
]
172+
}
173+
}
174+
]
175+
}
176+
```
177+
57178
### App Settings
58179

59180
| Setting | Description |
@@ -195,15 +316,19 @@ docker run `
195316

196317
### Docker Compose
197318

198-
There is a `docker-compose.yml` file in the src folder that you can use (or modify) to build your own Docker image.
319+
There is a `docker-compose.yml` file in the repo root that you can use to build and run the simulator along with an Azure Service Bus emulator for local development.
199320

200321
```
201-
docker-compose up --build `
202-
--force-recreate `
203-
--remove-orphans `
204-
--detach
322+
docker-compose up --build --detach
205323
```
206324

325+
The Docker Compose setup includes:
326+
- **Azure Event Grid Simulator** - The main simulator
327+
- **Azure Service Bus Emulator** - For testing Service Bus subscribers locally
328+
- **SQL Server** - Required by the Service Bus emulator
329+
330+
See `docker/appsettings.docker.json` for an example configuration with both HTTP and Service Bus subscribers.
331+
207332
## Using the Simulator
208333

209334
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`.
@@ -372,10 +497,11 @@ dotnet csharpier format src
372497

373498
## Future Development
374499

375-
Some features that could be added if there was a need for them: -
500+
Some features that could be added if there was a need for them:
376501

377502
- Subscriber retries & dead lettering. https://docs.microsoft.com/en-us/azure/event-grid/delivery-and-retry
378503
- Certificate configuration in `appsettings.json`.
379-
- Subscriber token auth
380-
- Better Docker support.
381-
- Maybe a web based console for admin stats etc.
504+
- Subscriber token auth.
505+
- Azure Event Hub subscriber support.
506+
- Azure Storage Queue subscriber support.
507+
- Web-based console for admin stats etc.

docker-compose.yml

Lines changed: 44 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
1-
version: "3.9"
21
services:
3-
azureventgridsimulator:
2+
azureeventgridsimulator:
43
image: azureeventgridsimulator:dev
54
container_name: azureeventgridsimulator-dev
65
ports:
@@ -12,36 +11,61 @@ services:
1211
- ./docker:/aegs:ro
1312
environment:
1413
- ASPNETCORE_ENVIRONMENT=Development
15-
# specify cert details note: can be generated like so:
14+
# specify cert details note: can be generated like so:
1615
# dotnet dev-certs https --export-path ./docker/azureEventGridSimulator.pfx --password Y0urSup3rCrypt1cPa55w0rd!
1716
- ASPNETCORE_Kestrel__Certificates__Default__Path=/aegs/azureEventGridSimulator.pfx
1817
- ASPNETCORE_Kestrel__Certificates__Default__Password=Y0urSup3rCrypt1cPa55w0rd!
1918

20-
# example of how to configure a topic via environment variables
21-
- AEGS_Topics__0__name=ExampleTopic
22-
- AEGS_Topics__0__port=60101
23-
- AEGS_Topics__0__key=TheLocal+DevelopmentKey=
24-
# add 'request catcher' subscriber
25-
- AEGS_Topics__0__subscribers__0__name=RequestCatcherSubscription
26-
- AEGS_Topics__0__subscribers__0__endpoint=https://azureeventgridsimulator.requestcatcher.com/
27-
- AEGS_Topics__0__subscribers__0__disableValidation=true
28-
# add an Azure Function subscriber running on localhost (host.docker.internal)
29-
- AEGS_Topics__0__subscribers__1__name=AzureFunctionSubscription
30-
- AEGS_Topics__0__subscribers__1__endpoint=http://host.docker.internal:7071/runtime/webhooks/EventGrid?functionName=ExampleFunction
31-
- AEGS_Topics__0__subscribers__1__disableValidation=false
19+
# you could also define topics/subscribers via via a configfile
20+
- AEGS_ConfigFile=/aegs/appsettings.docker.json
21+
3222
# logging configuration
3323
- AEGS_Serilog__MinimumLevel__Default=Verbose
3424

35-
# you could also define topics/subscribers via via a configfile
36-
# - AEGS_ConfigFile=/aegs/appsettings.docker.json
25+
depends_on:
26+
- servicebus-emulator
3727

38-
#env_file:
39-
# you can also define environment variables via a file
40-
#- ./my.env
28+
networks:
29+
- aegs-network
4130

4231
build:
4332
context: .
4433
dockerfile: ./Dockerfile
4534

35+
# Azure Service Bus Emulator
36+
# See: https://learn.microsoft.com/en-us/azure/service-bus-messaging/test-locally-with-service-bus-emulator
37+
servicebus-emulator:
38+
container_name: servicebus-emulator
39+
image: mcr.microsoft.com/azure-messaging/servicebus-emulator:latest
40+
pull_policy: always
41+
volumes:
42+
- ./docker/servicebus-config.json:/ServiceBus_Emulator/ConfigFiles/Config.json
43+
ports:
44+
- "5672:5672" # AMQP port
45+
environment:
46+
SQL_SERVER: mssql
47+
MSSQL_SA_PASSWORD: ${MSSQL_SA_PASSWORD:-YourStr0ngP@ssword!}
48+
ACCEPT_EULA: "Y"
49+
depends_on:
50+
- mssql
51+
networks:
52+
aegs-network:
53+
aliases:
54+
- servicebus-emulator
55+
56+
mssql:
57+
container_name: mssql
58+
image: mcr.microsoft.com/mssql/server:2022-latest
59+
environment:
60+
ACCEPT_EULA: "Y"
61+
MSSQL_SA_PASSWORD: ${MSSQL_SA_PASSWORD:-YourStr0ngP@ssword!}
62+
networks:
63+
aegs-network:
64+
aliases:
65+
- mssql
66+
67+
networks:
68+
aegs-network:
69+
4670
volumes:
4771
docker:

docker/appsettings.docker.json

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
{
2+
"topics": [
3+
{
4+
"name": "ExampleTopic",
5+
"port": 60101,
6+
"key": "TheLocal+DevelopmentKey=",
7+
"subscribers": {
8+
"http": [
9+
{
10+
"name": "RequestCatcherSubscription",
11+
"endpoint": "https://azureeventgridsimulator.requestcatcher.com/",
12+
"disableValidation": true
13+
}
14+
],
15+
"serviceBus": [
16+
{
17+
"name": "ServiceBusQueueSubscription",
18+
"connectionString": "Endpoint=sb://servicebus-emulator;SharedAccessKeyName=RootManageSharedAccessKey;SharedAccessKey=SAS_KEY_VALUE;UseDevelopmentEmulator=true;",
19+
"queue": "eventgrid-events",
20+
"properties": {
21+
"EventType": {
22+
"type": "dynamic",
23+
"value": "EventType"
24+
},
25+
"Subject": {
26+
"type": "dynamic",
27+
"value": "Subject"
28+
},
29+
"Source": {
30+
"type": "static",
31+
"value": "AzureEventGridSimulator"
32+
}
33+
}
34+
},
35+
{
36+
"name": "ServiceBusTopicSubscription",
37+
"connectionString": "Endpoint=sb://servicebus-emulator;SharedAccessKeyName=RootManageSharedAccessKey;SharedAccessKey=SAS_KEY_VALUE;UseDevelopmentEmulator=true;",
38+
"topic": "eventgrid-topic",
39+
"properties": {
40+
"EventId": {
41+
"type": "dynamic",
42+
"value": "Id"
43+
}
44+
}
45+
}
46+
]
47+
}
48+
}
49+
]
50+
}

0 commit comments

Comments
 (0)