-
Notifications
You must be signed in to change notification settings - Fork 0
Add Dapr Outbox pattern #319
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
base: main
Are you sure you want to change the base?
Changes from all commits
271bf69
9c1f402
5ed2482
ca04e53
2712740
f037dfb
df070f8
e548f97
af5d100
455ce97
8befeb4
992cda2
213f330
972aaee
5ca3ca3
4bc3e58
e2aca33
562c0b7
dd23cce
99fe5a2
8f495fd
d33c2c2
eb4bdc7
b6f77d9
8a515a9
dc95925
269f8f7
dae9004
545c3e2
841e5e6
f4cc0c8
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -0,0 +1,294 @@ | ||||||||||||||
| using System.Collections; | ||||||||||||||
| using Autofac; | ||||||||||||||
| using FrontDesk.Eventing; | ||||||||||||||
| using Microsoft.AspNetCore.Mvc; | ||||||||||||||
| using System.Text.Json; | ||||||||||||||
|
|
||||||||||||||
| namespace FrontDesk.Api.Endpoints; | ||||||||||||||
|
|
||||||||||||||
| public static class DaprEventEndpoints | ||||||||||||||
| { | ||||||||||||||
| public static void MapDaprEventEndpoints(this IEndpointRouteBuilder app) | ||||||||||||||
| { | ||||||||||||||
| // Dapr subscription endpoint - restricted to Dapr runtime only | ||||||||||||||
| app.MapGet("/dapr/subscribe", GetSubscriptions) | ||||||||||||||
| .RequireHost("localhost", "127.0.0.1") // Only allow local requests from Dapr sidecar | ||||||||||||||
| .AllowAnonymous() | ||||||||||||||
| .ExcludeFromDescription(); | ||||||||||||||
|
|
||||||||||||||
| // Generic event handler endpoint - restricted to Dapr runtime only | ||||||||||||||
| app.MapPost("/events/{eventType}", HandleEvent) | ||||||||||||||
| .RequireHost("localhost", "127.0.0.1") // Only allow local requests from Dapr sidecar | ||||||||||||||
| .AllowAnonymous() | ||||||||||||||
| .ExcludeFromDescription(); | ||||||||||||||
|
|
||||||||||||||
| // Outbox processing endpoint triggered by Dapr cron binding | ||||||||||||||
| app.MapPost("/outbox/process", ProcessOutbox) | ||||||||||||||
| .RequireHost("localhost", "127.0.0.1") // Only allow local requests from Dapr sidecar | ||||||||||||||
| .AllowAnonymous() | ||||||||||||||
| .ExcludeFromDescription(); | ||||||||||||||
|
|
||||||||||||||
| // Dapr input binding endpoint for cron-triggered outbox processing | ||||||||||||||
| app.MapPost("/frontdesk-outbox-cron", ProcessOutboxBinding) | ||||||||||||||
| .RequireHost("localhost", "127.0.0.1") // Only allow local requests from Dapr sidecar | ||||||||||||||
| .AllowAnonymous() | ||||||||||||||
| .ExcludeFromDescription(); | ||||||||||||||
| } | ||||||||||||||
|
|
||||||||||||||
| private static IResult GetSubscriptions(HttpContext context, ILogger<DaprEventPublisher> logger) | ||||||||||||||
| { | ||||||||||||||
| // Enhanced security validation for Dapr runtime access | ||||||||||||||
| var remoteIp = context.Connection.RemoteIpAddress?.ToString(); | ||||||||||||||
|
|
||||||||||||||
| // Verify request is from local Dapr sidecar | ||||||||||||||
| if (!IsLocalRequest(remoteIp)) | ||||||||||||||
| { | ||||||||||||||
| logger.LogWarning("Subscription endpoint accessed from non-local IP: {RemoteIP}", remoteIp); | ||||||||||||||
| return Results.Forbid(); | ||||||||||||||
| } | ||||||||||||||
|
|
||||||||||||||
| // TODO: we need to add dapr app token for extra security | ||||||||||||||
|
||||||||||||||
|
|
||||||||||||||
| logger.LogDebug("Providing Dapr subscriptions to {RemoteIP}", remoteIp); | ||||||||||||||
|
|
||||||||||||||
| var subscriptions = new[] | ||||||||||||||
| { | ||||||||||||||
| new | ||||||||||||||
| { | ||||||||||||||
| pubsubname = "frontdesk-pubsub", | ||||||||||||||
| topic = "ReservationCreated", | ||||||||||||||
| route = "/events/ReservationCreated" | ||||||||||||||
| }, | ||||||||||||||
| new | ||||||||||||||
| { | ||||||||||||||
| pubsubname = "frontdesk-pubsub", | ||||||||||||||
| topic = "ReservationUpdated", | ||||||||||||||
| route = "/events/ReservationUpdated" | ||||||||||||||
| }, | ||||||||||||||
| new | ||||||||||||||
| { | ||||||||||||||
| pubsubname = "frontdesk-pubsub", | ||||||||||||||
| topic = "ReservationDeleted", | ||||||||||||||
| route = "/events/ReservationDeleted" | ||||||||||||||
| }, | ||||||||||||||
| new | ||||||||||||||
| { | ||||||||||||||
| pubsubname = "frontdesk-pubsub", | ||||||||||||||
|
Comment on lines
+71
to
+76
|
||||||||||||||
| topic = "ReservationDeleted", | |
| route = "/events/ReservationDeleted" | |
| }, | |
| new | |
| { | |
| pubsubname = "frontdesk-pubsub", |
Copilot
AI
Dec 23, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The same event type discovery logic using reflection is duplicated here (lines 145-161) and in DaprOutboxService.cs (lines 252-291). This code duplication violates DRY principles and makes maintenance harder. Consider extracting this logic into a shared service or helper class that caches the type mappings.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The GitHub Actions workflow is being changed to deploy Docker images using
images: 'docker-compose.yml', but docker-compose.yml references an image that doesn't exist yet:myacr.azurecr.io/frontdesk-api:${{ github.sha }}. This variable syntax is for GitHub Actions, not Docker Compose. The workflow needs to build and push the Docker image first, and the docker-compose.yml should use a proper environment variable reference like${IMAGE_TAG}or the workflow should provide a different deployment mechanism.