< Summary

Information
Class: AsiBackbone.AspNetCore.Endpoints.AsiBackboneEndpointGovernanceDescriptor
Assembly: AsiBackbone.AspNetCore
File(s): /home/runner/work/AsiBackbone/AsiBackbone/src/AsiBackbone.AspNetCore/Endpoints/AsiBackboneEndpointGovernanceDescriptor.cs
Line coverage
90%
Covered lines: 73
Uncovered lines: 8
Coverable lines: 81
Total lines: 171
Line coverage: 90.1%
Branch coverage
90%
Covered branches: 27
Total branches: 30
Branch coverage: 90%
Method coverage

Feature is only available for sponsors

Upgrade to PRO version

Metrics

MethodBranch coverage Crap Score Cyclomatic complexity Line coverage
.cctor()100%11100%
.ctor(...)100%11100%
get_OperationName()100%11100%
get_PolicyTypes()100%11100%
get_ShortCircuitOnFirstDenial()100%11100%
get_RequiresLiabilityHandshake()100%11100%
get_CapabilityScopes()100%11100%
get_EmitGovernanceAudit()100%11100%
get_HasGovernanceMetadata()100%88100%
FromEndpoint(...)83.33%6696.66%
None(...)100%210%
ToMetadata()92.85%1414100%
ResolveOperationName(...)50%22100%

File(s)

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

#LineLine coverage
 1using System.Collections.ObjectModel;
 2using Microsoft.AspNetCore.Http;
 3
 4namespace AsiBackbone.AspNetCore.Endpoints;
 5
 6/// <summary>
 7/// Represents normalized AsiBackbone governance metadata resolved from an ASP.NET Core endpoint.
 8/// </summary>
 9public sealed class AsiBackboneEndpointGovernanceDescriptor
 10{
 311    private static readonly ReadOnlyCollection<Type> EmptyPolicyTypes = Array.AsReadOnly(Array.Empty<Type>());
 312    private static readonly ReadOnlyCollection<string> EmptyScopes = Array.AsReadOnly(Array.Empty<string>());
 13
 3514    private AsiBackboneEndpointGovernanceDescriptor(
 3515        string operationName,
 3516        IReadOnlyList<Type> policyTypes,
 3517        bool? shortCircuitOnFirstDenial,
 3518        bool requiresLiabilityHandshake,
 3519        IReadOnlyList<string> capabilityScopes,
 3520        bool emitGovernanceAudit)
 21    {
 3522        ArgumentException.ThrowIfNullOrWhiteSpace(operationName);
 23
 3524        OperationName = operationName.Trim();
 3525        PolicyTypes = policyTypes;
 3526        ShortCircuitOnFirstDenial = shortCircuitOnFirstDenial;
 3527        RequiresLiabilityHandshake = requiresLiabilityHandshake;
 3528        CapabilityScopes = capabilityScopes;
 3529        EmitGovernanceAudit = emitGovernanceAudit;
 3530    }
 31
 32    /// <summary>
 33    /// Gets the operation name used for audit residue and acknowledgment challenge construction.
 34    /// </summary>
 2435    public string OperationName { get; }
 36
 37    /// <summary>
 38    /// Gets the policy marker or resolver types attached to the endpoint.
 39    /// </summary>
 7840    public IReadOnlyList<Type> PolicyTypes { get; }
 41
 42    /// <summary>
 43    /// Gets an endpoint-scoped first-denial short-circuit preference, when endpoint metadata supplied one.
 44    /// </summary>
 4945    public bool? ShortCircuitOnFirstDenial { get; }
 46
 47    /// <summary>
 48    /// Gets a value indicating whether liability-handshake support is requested.
 49    /// </summary>
 3750    public bool RequiresLiabilityHandshake { get; }
 51
 52    /// <summary>
 53    /// Gets the required capability-grant scopes attached to the endpoint.
 54    /// </summary>
 5755    public IReadOnlyList<string> CapabilityScopes { get; }
 56
 57    /// <summary>
 58    /// Gets a value indicating whether governance audit emission is requested.
 59    /// </summary>
 3060    public bool EmitGovernanceAudit { get; }
 61
 62    /// <summary>
 63    /// Gets a value indicating whether the endpoint contains any AsiBackbone governance metadata.
 64    /// </summary>
 3565    public bool HasGovernanceMetadata => PolicyTypes.Count > 0
 3566        || ShortCircuitOnFirstDenial.HasValue
 3567        || RequiresLiabilityHandshake
 3568        || CapabilityScopes.Count > 0
 3569        || EmitGovernanceAudit;
 70
 71    /// <summary>
 72    /// Creates a descriptor from the selected ASP.NET Core endpoint.
 73    /// </summary>
 74    /// <param name="endpoint">The selected endpoint.</param>
 75    /// <returns>A normalized descriptor.</returns>
 76    public static AsiBackboneEndpointGovernanceDescriptor FromEndpoint(Endpoint? endpoint)
 77    {
 3578        if (endpoint is null)
 79        {
 080            return None("unresolved-endpoint");
 81        }
 82
 3583        Type[] policyTypes = [.. endpoint.Metadata
 3584            .GetOrderedMetadata<IAsiBackboneEndpointGovernancePolicyMetadata>()
 1585            .Select(metadata => metadata.PolicyType)
 1586            .Where(static policyType => policyType is not null)
 3587            .Distinct()];
 88
 3589        string[] capabilityScopes = [.. endpoint.Metadata
 3590            .GetOrderedMetadata<IAsiBackboneEndpointCapabilityGrantMetadata>()
 1891            .Select(metadata => metadata.Scope)
 1892            .Where(static scope => !string.IsNullOrWhiteSpace(scope))
 1893            .Select(static scope => scope.Trim())
 3594            .Distinct(StringComparer.Ordinal)];
 95
 3596        bool? shortCircuitOnFirstDenial = endpoint.Metadata
 3597            .GetOrderedMetadata<IAsiBackboneEndpointPolicyEvaluationOptionsMetadata>()
 498            .Select(static metadata => metadata.ShortCircuitOnFirstDenial)
 3599            .LastOrDefault();
 100
 35101        bool requiresLiabilityHandshake = endpoint.Metadata
 35102            .GetOrderedMetadata<IAsiBackboneEndpointLiabilityHandshakeMetadata>()
 37103            .Any(static metadata => metadata.RequiresLiabilityHandshake);
 104
 35105        bool emitGovernanceAudit = endpoint.Metadata
 35106            .GetOrderedMetadata<IAsiBackboneEndpointAuditEmissionMetadata>()
 38107            .Any(static metadata => metadata.EmitGovernanceAudit);
 108
 35109        return new AsiBackboneEndpointGovernanceDescriptor(
 35110            ResolveOperationName(endpoint),
 35111            policyTypes.Length == 0 ? EmptyPolicyTypes : Array.AsReadOnly(policyTypes),
 35112            shortCircuitOnFirstDenial,
 35113            requiresLiabilityHandshake,
 35114            capabilityScopes.Length == 0 ? EmptyScopes : Array.AsReadOnly(capabilityScopes),
 35115            emitGovernanceAudit);
 116    }
 117
 118    /// <summary>
 119    /// Creates a descriptor that does not request governance handling.
 120    /// </summary>
 121    /// <param name="operationName">The operation name to associate with the descriptor.</param>
 122    /// <returns>A descriptor with no governance metadata.</returns>
 123    public static AsiBackboneEndpointGovernanceDescriptor None(string operationName)
 124    {
 0125        return new AsiBackboneEndpointGovernanceDescriptor(
 0126            operationName,
 0127            EmptyPolicyTypes,
 0128            shortCircuitOnFirstDenial: null,
 0129            requiresLiabilityHandshake: false,
 0130            EmptyScopes,
 0131            emitGovernanceAudit: false);
 132    }
 133
 134    /// <summary>
 135    /// Converts descriptor values into safe metadata for framework-neutral governance evaluation and audit residue.
 136    /// </summary>
 137    /// <returns>A normalized metadata dictionary.</returns>
 138    public IReadOnlyDictionary<string, string> ToMetadata()
 139    {
 17140        Dictionary<string, string> metadata = new(StringComparer.Ordinal)
 17141        {
 17142            ["endpoint.operation_name"] = OperationName,
 17143            ["endpoint.requires_liability_handshake"] = RequiresLiabilityHandshake ? "true" : "false",
 17144            ["endpoint.emit_governance_audit"] = EmitGovernanceAudit ? "true" : "false"
 17145        };
 146
 17147        if (PolicyTypes.Count > 0)
 148        {
 22149            metadata["endpoint.policy_types"] = string.Join(",", PolicyTypes.Select(static policyType => policyType.Full
 150        }
 151
 17152        if (ShortCircuitOnFirstDenial.HasValue)
 153        {
 4154            metadata["endpoint.short_circuit_on_first_denial"] = ShortCircuitOnFirstDenial.Value ? "true" : "false";
 155        }
 156
 17157        if (CapabilityScopes.Count > 0)
 158        {
 10159            metadata["endpoint.capability_scopes"] = string.Join(",", CapabilityScopes);
 160        }
 161
 17162        return metadata;
 163    }
 164
 165    private static string ResolveOperationName(Endpoint endpoint)
 166    {
 35167        return string.IsNullOrWhiteSpace(endpoint.DisplayName)
 35168            ? "aspnetcore.endpoint"
 35169            : endpoint.DisplayName.Trim();
 170    }
 171}