Skip to content

Commit e342cdc

Browse files
merge pull request #7 from https-richardy/feature/06-implement-correlation-support
[#6] - implements correlation support
2 parents f7e6043 + b428ae5 commit e342cdc

35 files changed

Lines changed: 353 additions & 3 deletions

File tree

Boundaries/Comanda.Orchestrator/Source/Comanda.Orchestrator.WebApi/Constants/Headers.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,4 +8,5 @@ public static class Headers
88
public const string Pagination = "X-Pagination";
99
public const string Authorization = "Authorization";
1010
public const string Idempotency = "Idempotency-Key";
11+
public const string Correlation = "Correlation";
1112
}

Boundaries/Comanda.Orchestrator/Source/Comanda.Orchestrator.WebApi/Extensions/ClientsExtension.cs

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ public static void AddHttpClients(this IServiceCollection services)
99

1010
// registers the header propagation service
1111
// essential for receiving an authenticated request and forwarding it to another service
12+
services.AddTransient<CorrelationInterceptor>();
1213
services.AddHeaderPropagation(options =>
1314
{
1415
options.Headers.Add(Headers.Authorization);
@@ -63,14 +64,27 @@ public static void AddHttpClients(this IServiceCollection services)
6364
});
6465

6566
customersClient.AddHeaderPropagation();
67+
customersClient.AddHttpMessageHandler<CorrelationInterceptor>();
68+
6669
ownersClient.AddHeaderPropagation();
70+
ownersClient.AddHttpMessageHandler<CorrelationInterceptor>();
6771

6872
paymentsClient.AddHeaderPropagation();
73+
paymentsClient.AddHttpMessageHandler<CorrelationInterceptor>();
74+
6975
storesClient.AddHeaderPropagation();
76+
storesClient.AddHttpMessageHandler<CorrelationInterceptor>();
77+
7078
productsClient.AddHeaderPropagation();
79+
productsClient.AddHttpMessageHandler<CorrelationInterceptor>();
7180

7281
subscriptionsClient.AddHeaderPropagation();
82+
subscriptionsClient.AddHttpMessageHandler<CorrelationInterceptor>();
83+
7384
ordersClient.AddHeaderPropagation();
85+
ordersClient.AddHttpMessageHandler<CorrelationInterceptor>();
86+
7487
credentialsClient.AddHeaderPropagation();
88+
credentialsClient.AddHttpMessageHandler<CorrelationInterceptor>();
7589
}
7690
}

Boundaries/Comanda.Orchestrator/Source/Comanda.Orchestrator.WebApi/Extensions/HttpPipelineExtension.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,8 @@ public static void UseHttpPipeline(this IApplicationBuilder app)
1515
app.UseAuthorization();
1616

1717
app.UsePrincipalMiddleware();
18+
app.UseCorrelationMiddleware();
19+
1820
app.UseEndpoints(endpoints =>
1921
{
2022
endpoints.MapControllers();
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
namespace Comanda.Orchestrator.WebApi.Interceptors;
2+
3+
public sealed class CorrelationInterceptor(IHttpContextAccessor accessor) : DelegatingHandler
4+
{
5+
protected override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
6+
{
7+
var correlation = accessor.HttpContext?.Items[Headers.Correlation]?.ToString();
8+
9+
if (!string.IsNullOrWhiteSpace(correlation))
10+
{
11+
request.Headers.Remove(Headers.Correlation);
12+
request.Headers.Add(Headers.Correlation, correlation);
13+
}
14+
15+
return await base.SendAsync(request, cancellationToken);
16+
}
17+
}
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
namespace Comanda.Orchestrator.WebApi.Middlewares;
2+
3+
public sealed class CorrelationMiddleware(RequestDelegate next)
4+
{
5+
public async Task InvokeAsync(HttpContext context)
6+
{
7+
var correlationId = context.Request.Headers[Headers.Correlation].FirstOrDefault();
8+
9+
if (string.IsNullOrWhiteSpace(correlationId))
10+
correlationId = context.TraceIdentifier;
11+
12+
context.Items[Headers.Correlation] = correlationId;
13+
context.Response.Headers[Headers.Correlation] = correlationId;
14+
15+
/* enriches the logging scope with the current correlation identifier, ensuring all logs within this request pipeline share the same identifier */
16+
/* more details: https://microsoft.github.io/code-with-engineering-playbook/observability/correlation-id/ */
17+
18+
using (LogContext.PushProperty(Headers.Correlation, correlationId))
19+
using (SentrySdk.PushScope())
20+
{
21+
SentrySdk.ConfigureScope(scope =>
22+
{
23+
scope.SetTag("correlation_id", correlationId);
24+
});
25+
26+
await next(context);
27+
}
28+
}
29+
}
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
namespace Comanda.Orchestrator.WebApi.Middlewares;
2+
3+
[ExcludeFromCodeCoverage(Justification = "contains only dependency injection")]
4+
public static class CorrelationMiddlewareExtension
5+
{
6+
public static IApplicationBuilder UseCorrelationMiddleware(this IApplicationBuilder app)
7+
{
8+
return app.UseMiddleware<CorrelationMiddleware>();
9+
}
10+
}

Boundaries/Comanda.Orchestrator/Source/Comanda.Orchestrator.WebApi/Usings.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
global using Comanda.Orchestrator.WebApi.Extensions;
2222
global using Comanda.Orchestrator.WebApi.Constants;
2323
global using Comanda.Orchestrator.WebApi.Middlewares;
24+
global using Comanda.Orchestrator.WebApi.Interceptors;
2425
global using Comanda.Orchestrator.WebApi.Providers;
2526

2627
global using Comanda.Orchestrator.Application.Payloads.Payments;
@@ -38,5 +39,6 @@
3839

3940
global using Scalar.AspNetCore;
4041
global using Serilog;
42+
global using Serilog.Context;
4143
global using FluentValidation.AspNetCore;
4244

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
namespace Comanda.Orders.WebApi.Constants;
2+
3+
public static class Headers
4+
{
5+
public const string Credential = "X-Credential";
6+
public const string Location = "Location";
7+
public const string Link = "Link";
8+
public const string Pagination = "X-Pagination";
9+
public const string Authorization = "Authorization";
10+
public const string Idempotency = "Idempotency-Key";
11+
public const string Correlation = "Correlation";
12+
}

Boundaries/Comanda.Orders/Source/Comanda.Orders.WebApi/Extensions/HttpPipelineExtension.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ public static void UseHttpPipeline(this IApplicationBuilder app)
1313
app.UseAuthentication();
1414
app.UseAuthorization();
1515

16+
app.UseCorrelationMiddleware();
1617
app.UseEndpoints(endpoints =>
1718
{
1819
endpoints.MapControllers();
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
namespace Comanda.Orders.WebApi.Middlewares;
2+
3+
public sealed class CorrelationMiddleware(RequestDelegate next)
4+
{
5+
public async Task InvokeAsync(HttpContext context)
6+
{
7+
var correlationId = context.Request.Headers[Headers.Correlation].FirstOrDefault();
8+
9+
if (string.IsNullOrWhiteSpace(correlationId))
10+
correlationId = context.TraceIdentifier;
11+
12+
context.Items[Headers.Correlation] = correlationId;
13+
context.Response.Headers[Headers.Correlation] = correlationId;
14+
15+
/* enriches the logging scope with the current correlation identifier, ensuring all logs within this request pipeline share the same identifier */
16+
/* more details: https://microsoft.github.io/code-with-engineering-playbook/observability/correlation-id/ */
17+
18+
using (LogContext.PushProperty(Headers.Correlation, correlationId))
19+
using (SentrySdk.PushScope())
20+
{
21+
SentrySdk.ConfigureScope(scope =>
22+
{
23+
scope.SetTag("correlation_id", correlationId);
24+
});
25+
26+
await next(context);
27+
}
28+
}
29+
}

0 commit comments

Comments
 (0)