< Summary

Information
Class: AsiBackbone.AspNetCore.Endpoints.AsiBackboneEndpointGovernanceMiddleware
Assembly: AsiBackbone.AspNetCore
File(s): /home/runner/work/AsiBackbone/AsiBackbone/src/AsiBackbone.AspNetCore/Endpoints/AsiBackboneEndpointGovernanceMiddleware.cs
Line coverage
96%
Covered lines: 51
Uncovered lines: 2
Coverable lines: 53
Total lines: 108
Line coverage: 96.2%
Branch coverage
77%
Covered branches: 17
Total branches: 22
Branch coverage: 77.2%
Method coverage

Feature is only available for sponsors

Upgrade to PRO version

Metrics

MethodBranch coverage Crap Score Cyclomatic complexity Line coverage
.ctor(...)50%22100%
.cctor()100%11100%
InvokeAsync()83.33%121294.28%
CreateDefaultForbiddenResult(...)75%88100%

File(s)

/home/runner/work/AsiBackbone/AsiBackbone/src/AsiBackbone.AspNetCore/Endpoints/AsiBackboneEndpointGovernanceMiddleware.cs

#LineLine coverage
 1using Microsoft.AspNetCore.Http;
 2using Microsoft.Extensions.Options;
 3
 4namespace AsiBackbone.AspNetCore.Endpoints;
 5
 6/// <summary>
 7/// Middleware that evaluates AsiBackbone endpoint governance metadata before endpoint execution.
 8/// </summary>
 9/// <remarks>
 10/// Initializes a new instance of the <see cref="AsiBackboneEndpointGovernanceMiddleware" /> class.
 11/// </remarks>
 12/// <param name="next">The next request delegate.</param>
 2013public sealed class AsiBackboneEndpointGovernanceMiddleware(RequestDelegate next)
 14{
 15    // Example: use a precomputed static result for the most common forbidden response
 216    private static readonly IResult LightweightForbiddenResult =
 217        Microsoft.AspNetCore.Http.Results.StatusCode(StatusCodes.Status403Forbidden);
 18    // Use this for default cases instead of constructing new ProblemDetails every time.
 19
 2020    private readonly RequestDelegate next = next ?? throw new ArgumentNullException(nameof(next));
 21
 22    /// <summary>
 23    /// Evaluates endpoint governance metadata and either continues execution or writes a failure result.
 24    /// </summary>
 25    /// <param name="httpContext">The current HTTP context.</param>
 26    /// <param name="governanceService">The endpoint governance service.</param>
 27    /// <param name="endpointOptions">The endpoint governance options.</param>
 28    /// <returns>A task that completes when the middleware has run.</returns>
 29    public async Task InvokeAsync(
 30        HttpContext httpContext,
 31        IAsiBackboneEndpointGovernanceService governanceService,
 32        IOptions<AsiBackboneEndpointGovernanceOptions> endpointOptions)
 33    {
 2034        ArgumentNullException.ThrowIfNull(httpContext);
 2035        ArgumentNullException.ThrowIfNull(governanceService);
 2036        ArgumentNullException.ThrowIfNull(endpointOptions);
 37
 2038        AsiBackboneEndpointGovernanceOptions options = endpointOptions.Value;
 2039        options.Validate();
 40
 2041        Endpoint? endpoint = httpContext.GetEndpoint();
 2042        var descriptor = AsiBackboneEndpointGovernanceDescriptor.FromEndpoint(endpoint);
 43
 2044        bool endpointAllowsMissingGovernance = endpoint?.Metadata
 2045            .GetMetadata<AllowMissingGovernanceMetadataAttribute>() is not null;
 46
 2047        if (!descriptor.HasGovernanceMetadata)
 48        {
 649            if (options.RequireGovernanceMetadata && !endpointAllowsMissingGovernance)
 50            {
 251                IResult missingGovernanceResult = CreateDefaultForbiddenResult(
 252                    httpContext,
 253                    options,
 254                    descriptor,
 255                    result: null,
 256                    decisionStage: "aspnetcore.endpoint.governance.metadata");
 57
 258                await missingGovernanceResult.ExecuteAsync(httpContext).ConfigureAwait(false);
 259                return;
 60            }
 61
 462            await next(httpContext).ConfigureAwait(false);
 463            return;
 64        }
 65
 1466        AsiBackboneEndpointGovernanceResult result = await governanceService
 1467            .EvaluateAsync(httpContext, descriptor, httpContext.RequestAborted)
 1468            .ConfigureAwait(false);
 69
 1470        if (result.CanExecute)
 71        {
 072            await next(httpContext).ConfigureAwait(false);
 073            return;
 74        }
 75
 1476        IResult failureResult = result.FailureResult ?? CreateDefaultForbiddenResult(
 1477            httpContext,
 1478            options,
 1479            descriptor,
 1480            result,
 1481            "aspnetcore.endpoint.governance.decision");
 82
 1483        await failureResult.ExecuteAsync(httpContext).ConfigureAwait(false);
 2084    }
 85
 86    private static IResult CreateDefaultForbiddenResult(
 87        HttpContext httpContext,
 88        AsiBackboneEndpointGovernanceOptions options,
 89        AsiBackboneEndpointGovernanceDescriptor descriptor,
 90        AsiBackboneEndpointGovernanceResult? result,
 91        string decisionStage)
 92    {
 1293        return AsiBackboneEndpointGovernanceDevelopmentDiagnostics.IsEnabled(httpContext, options)
 1294            ? AsiBackboneEndpointGovernanceDevelopmentDiagnostics.CreateProblem(
 1295                httpContext,
 1296                options,
 1297                descriptor,
 1298                result?.Decision,
 1299                decisionStage,
 12100                title: "Endpoint governance blocked execution.",
 12101                detail: "Endpoint governance blocked this request before the selected endpoint executed.",
 12102                statusCode: StatusCodes.Status403Forbidden)
 12103            : options.DefaultForbiddenResultFactory is null
 12104            ? LightweightForbiddenResult
 12105            : options.DefaultForbiddenResultFactory(httpContext)
 12106                ?? throw new InvalidOperationException($"{nameof(AsiBackboneEndpointGovernanceOptions.DefaultForbiddenRe
 107    }
 108}