< Summary

Information
Class: Intentum.AspNetCore.BehaviorObservationMiddleware
Assembly: Intentum.AspNetCore
File(s): /home/runner/work/Intentum/Intentum/src/Intentum.AspNetCore/BehaviorObservationMiddleware.cs
Line coverage
0%
Covered lines: 0
Uncovered lines: 33
Coverable lines: 33
Total lines: 73
Line coverage: 0%
Branch coverage
0%
Covered branches: 0
Total branches: 14
Branch coverage: 0%
Method coverage

Feature is only available for sponsors

Upgrade to PRO version

Metrics

MethodBranch coverage Cyclomatic complexity NPath complexity Sequence coverage
.ctor(...)0%660%
InvokeAsync()0%880%

File(s)

/home/runner/work/Intentum/Intentum/src/Intentum.AspNetCore/BehaviorObservationMiddleware.cs

#LineLine coverage
 1using Intentum.Core.Behavior;
 2using Microsoft.AspNetCore.Http;
 3
 4namespace Intentum.AspNetCore;
 5
 6/// <summary>
 7/// Middleware that automatically observes HTTP request behaviors into a BehaviorSpace.
 8/// </summary>
 9public sealed class BehaviorObservationMiddleware
 10{
 11    private readonly RequestDelegate _next;
 12    private readonly BehaviorSpace _behaviorSpace;
 13    private readonly BehaviorObservationOptions _options;
 14
 015    public BehaviorObservationMiddleware(
 016        RequestDelegate next,
 017        BehaviorSpace behaviorSpace,
 018        BehaviorObservationOptions? options = null)
 019    {
 020        _next = next ?? throw new ArgumentNullException(nameof(next));
 021        _behaviorSpace = behaviorSpace ?? throw new ArgumentNullException(nameof(behaviorSpace));
 022        _options = options ?? new BehaviorObservationOptions();
 023    }
 24
 25    public async Task InvokeAsync(HttpContext context)
 026    {
 027        if (_options.Enabled)
 028        {
 029            var actor = _options.GetActor(context);
 030            var action = _options.GetAction(context);
 31
 032            var metadata = new Dictionary<string, object>
 033            {
 034                { "path", context.Request.Path.Value ?? "" },
 035                { "method", context.Request.Method },
 036                { "statusCode", context.Response.StatusCode }
 037            };
 38
 039            if (_options.IncludeHeaders && context.Request.Headers.Any())
 040            {
 041                metadata["headers"] = context.Request.Headers
 042                    .ToDictionary(h => h.Key, h => (object)h.Value.ToString());
 043            }
 44
 045            _behaviorSpace.Observe(new BehaviorEvent(
 046                actor,
 047                action,
 048                DateTimeOffset.UtcNow,
 049                metadata));
 050        }
 51
 052        await _next(context);
 053    }
 54}
 55
 56/// <summary>
 57/// Options for behavior observation middleware.
 58/// </summary>
 59public sealed class BehaviorObservationOptions
 60{
 61    /// <summary>Whether observation is enabled. Defaults to true.</summary>
 62    public bool Enabled { get; set; } = true;
 63
 64    /// <summary>Whether to include request headers in metadata. Defaults to false.</summary>
 65    public bool IncludeHeaders { get; set; } = false;
 66
 67    /// <summary>Function to extract actor from HTTP context. Defaults to "http".</summary>
 68    public Func<HttpContext, string> GetActor { get; set; } = _ => "http";
 69
 70    /// <summary>Function to extract action from HTTP context. Defaults to method + path.</summary>
 71    public Func<HttpContext, string> GetAction { get; set; } = ctx =>
 72        $"{ctx.Request.Method.ToLowerInvariant()}_{ctx.Request.Path.Value?.Replace("/", "_").TrimStart('_') ?? "root"}";
 73}