< Summary

Information
Class: AsiBackbone.AspNetCore.Endpoints.EndpointGovernanceDescriptor
Assembly: AsiBackbone.AspNetCore
File(s): /home/runner/work/AsiBackbone/AsiBackbone/src/AsiBackbone.AspNetCore/Endpoints/EndpointGovernanceDescriptor.cs
Line coverage
93%
Covered lines: 110
Uncovered lines: 8
Coverable lines: 118
Total lines: 300
Line coverage: 93.2%
Branch coverage
88%
Covered branches: 69
Total branches: 78
Branch coverage: 88.4%
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_HasGovernanceMetadata()100%88100%
FromEndpoint(...)100%3232100%
None(...)100%11100%
ToMetadata()100%11100%
ToMetadata(...)75%4483.33%
ContainsPolicyType(...)83.33%7671.43%
ContainsScope(...)16.67%19628.57%
CreateFullMetadata()100%1212100%
CreateReducedMetadata(...)100%44100%
JoinPolicyTypeNames(...)75%44100%
ResolveOperationName(...)50%22100%

File(s)

/home/runner/work/AsiBackbone/AsiBackbone/src/AsiBackbone.AspNetCore/Endpoints/EndpointGovernanceDescriptor.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 EndpointGovernanceDescriptor
 10{
 211    private static readonly ReadOnlyCollection<Type> EmptyPolicyTypes = Array.AsReadOnly(Array.Empty<Type>());
 212    private static readonly ReadOnlyCollection<string> EmptyScopes = Array.AsReadOnly(Array.Empty<string>());
 13
 14    private readonly IReadOnlyDictionary<string, string> fullMetadata;
 15    private readonly IReadOnlyDictionary<string, string> reducedMetadata;
 16
 5217    private EndpointGovernanceDescriptor(
 5218        string operationName,
 5219        IReadOnlyList<Type> policyTypes,
 5220        bool? shortCircuitOnFirstDenial,
 5221        bool requiresAcknowledgment,
 5222        IReadOnlyList<string> capabilityScopes,
 5223        bool emitGovernanceAudit)
 24    {
 5225        ArgumentException.ThrowIfNullOrWhiteSpace(operationName);
 26
 5227        OperationName = operationName.Trim();
 5228        PolicyTypes = policyTypes;
 5229        ShortCircuitOnFirstDenial = shortCircuitOnFirstDenial;
 5230        RequiresAcknowledgment = requiresAcknowledgment;
 5231        CapabilityScopes = capabilityScopes;
 5232        EmitGovernanceAudit = emitGovernanceAudit;
 5233        reducedMetadata = CreateReducedMetadata(OperationName, PolicyTypes);
 5234        fullMetadata = CreateFullMetadata();
 5235    }
 36
 37    /// <summary>
 38    /// Gets the operation name used for decision receipt and acknowledgment challenge construction.
 39    /// </summary>
 40    public string OperationName { get; }
 41
 42    /// <summary>
 43    /// Gets the policy marker or resolver types attached to the endpoint.
 44    /// </summary>
 45    public IReadOnlyList<Type> PolicyTypes { get; }
 46
 47    /// <summary>
 48    /// Gets an endpoint-scoped first-denial short-circuit preference, when endpoint metadata supplied one.
 49    /// </summary>
 50    public bool? ShortCircuitOnFirstDenial { get; }
 51
 52    /// <summary>
 53    /// Gets a value indicating whether liability-handshake support is requested.
 54    /// </summary>
 55    public bool RequiresAcknowledgment { get; }
 56
 57    /// <summary>
 58    /// Gets the required capability-grant scopes attached to the endpoint.
 59    /// </summary>
 60    public IReadOnlyList<string> CapabilityScopes { get; }
 61
 62    /// <summary>
 63    /// Gets a value indicating whether governance audit emission is requested.
 64    /// </summary>
 65    public bool EmitGovernanceAudit { get; }
 66
 67    /// <summary>
 68    /// Gets a value indicating whether the endpoint contains any AsiBackbone governance metadata.
 69    /// </summary>
 4270    public bool HasGovernanceMetadata => PolicyTypes.Count > 0
 4271        || ShortCircuitOnFirstDenial.HasValue
 4272        || RequiresAcknowledgment
 4273        || CapabilityScopes.Count > 0
 4274        || EmitGovernanceAudit;
 75
 76    /// <summary>
 77    /// Creates a descriptor from the selected ASP.NET Core endpoint.
 78    /// </summary>
 79    /// <param name="endpoint">The selected endpoint.</param>
 80    /// <returns>A normalized descriptor.</returns>
 81    public static EndpointGovernanceDescriptor FromEndpoint(Endpoint? endpoint)
 82    {
 5183        if (endpoint is null)
 84        {
 485            return None("unresolved-endpoint");
 86        }
 87
 4788        List<Type>? policyTypes = null;
 15089        foreach (IEndpointGovernancePolicyMetadata metadata in endpoint.Metadata.GetOrderedMetadata<IEndpointGovernanceP
 90        {
 2891            if (metadata.PolicyType is not Type policyType || ContainsPolicyType(policyTypes, policyType))
 92            {
 93                continue;
 94            }
 95
 2896            policyTypes ??= [];
 2897            policyTypes.Add(policyType);
 98        }
 99
 47100        List<string>? capabilityScopes = null;
 134101        foreach (IEndpointCapabilityGrantMetadata metadata in endpoint.Metadata.GetOrderedMetadata<IEndpointCapabilityGr
 102        {
 20103            if (string.IsNullOrWhiteSpace(metadata.Scope))
 104            {
 105                continue;
 106            }
 107
 20108            string scope = metadata.Scope.Trim();
 20109            if (ContainsScope(capabilityScopes, scope))
 110            {
 111                continue;
 112            }
 113
 20114            capabilityScopes ??= [];
 20115            capabilityScopes.Add(scope);
 116        }
 117
 47118        bool? shortCircuitOnFirstDenial = null;
 100119        foreach (IEndpointPolicyEvaluationOptionsMetadata metadata in endpoint.Metadata.GetOrderedMetadata<IEndpointPoli
 120        {
 3121            shortCircuitOnFirstDenial = metadata.ShortCircuitOnFirstDenial;
 122        }
 123
 47124        bool requiresAcknowledgment = false;
 101125        foreach (IEndpointAcknowledgmentMetadata metadata in endpoint.Metadata.GetOrderedMetadata<IEndpointAcknowledgmen
 126        {
 7127            if (metadata.RequiresAcknowledgment)
 128            {
 7129                requiresAcknowledgment = true;
 7130                break;
 131            }
 132        }
 133
 47134        bool emitGovernanceAudit = false;
 101135        foreach (IEndpointAuditEmissionMetadata metadata in endpoint.Metadata.GetOrderedMetadata<IEndpointAuditEmissionM
 136        {
 7137            if (metadata.EmitGovernanceAudit)
 138            {
 7139                emitGovernanceAudit = true;
 7140                break;
 141            }
 142        }
 143
 47144        return new EndpointGovernanceDescriptor(
 47145            ResolveOperationName(endpoint),
 47146            policyTypes is null ? EmptyPolicyTypes : Array.AsReadOnly(policyTypes.ToArray()),
 47147            shortCircuitOnFirstDenial,
 47148            requiresAcknowledgment,
 47149            capabilityScopes is null ? EmptyScopes : Array.AsReadOnly(capabilityScopes.ToArray()),
 47150            emitGovernanceAudit);
 151    }
 152
 153    /// <summary>
 154    /// Creates a descriptor that does not request governance handling.
 155    /// </summary>
 156    /// <param name="operationName">The operation name to associate with the descriptor.</param>
 157    /// <returns>A descriptor with no governance metadata.</returns>
 158    public static EndpointGovernanceDescriptor None(string operationName)
 159    {
 5160        return new EndpointGovernanceDescriptor(
 5161            operationName,
 5162            EmptyPolicyTypes,
 5163            shortCircuitOnFirstDenial: null,
 5164            requiresAcknowledgment: false,
 5165            EmptyScopes,
 5166            emitGovernanceAudit: false);
 167    }
 168
 169    /// <summary>
 170    /// Converts descriptor values into safe metadata for framework-neutral governance evaluation and decision receipt.
 171    /// </summary>
 172    /// <returns>A normalized metadata dictionary.</returns>
 173    public IReadOnlyDictionary<string, string> ToMetadata()
 174    {
 7175        return ToMetadata(EndpointGovernanceMetadataMode.Full);
 176    }
 177
 178    /// <summary>
 179    /// Converts descriptor values into safe metadata for framework-neutral governance evaluation and decision receipt.
 180    /// </summary>
 181    /// <param name="metadataMode">The endpoint metadata mode to apply.</param>
 182    /// <returns>A normalized metadata dictionary.</returns>
 183    public IReadOnlyDictionary<string, string> ToMetadata(EndpointGovernanceMetadataMode metadataMode)
 184    {
 38185        return metadataMode switch
 38186        {
 31187            EndpointGovernanceMetadataMode.Full => fullMetadata,
 7188            EndpointGovernanceMetadataMode.Reduced => reducedMetadata,
 0189            _ => throw new ArgumentOutOfRangeException(nameof(metadataMode), metadataMode, "Endpoint governance metadata
 38190        };
 191    }
 192
 193    private static bool ContainsPolicyType(List<Type>? policyTypes, Type policyType)
 194    {
 28195        if (policyTypes is null)
 196        {
 27197            return false;
 198        }
 199
 4200        foreach (Type currentPolicyType in policyTypes)
 201        {
 1202            if (currentPolicyType == policyType)
 203            {
 0204                return true;
 205            }
 206        }
 207
 1208        return false;
 0209    }
 210
 211    private static bool ContainsScope(List<string>? capabilityScopes, string scope)
 212    {
 20213        if (capabilityScopes is null)
 214        {
 20215            return false;
 216        }
 217
 0218        foreach (string currentScope in capabilityScopes)
 219        {
 0220            if (string.Equals(currentScope, scope, StringComparison.Ordinal))
 221            {
 0222                return true;
 223            }
 224        }
 225
 0226        return false;
 0227    }
 228
 229    private ReadOnlyDictionary<string, string> CreateFullMetadata()
 230    {
 52231        int metadataCapacity = 3
 52232            + (PolicyTypes.Count > 0 ? 1 : 0)
 52233            + (ShortCircuitOnFirstDenial.HasValue ? 1 : 0)
 52234            + (CapabilityScopes.Count > 0 ? 1 : 0);
 235
 52236        var metadata = new Dictionary<string, string>(metadataCapacity, StringComparer.Ordinal)
 52237        {
 52238            ["endpoint.operation_name"] = OperationName,
 52239            ["endpoint.requires_liability_handshake"] = RequiresAcknowledgment ? "true" : "false",
 52240            ["endpoint.emit_governance_audit"] = EmitGovernanceAudit ? "true" : "false"
 52241        };
 242
 52243        if (PolicyTypes.Count > 0)
 244        {
 27245            metadata["endpoint.policy_types"] = JoinPolicyTypeNames(PolicyTypes);
 246        }
 247
 52248        if (ShortCircuitOnFirstDenial.HasValue)
 249        {
 2250            metadata["endpoint.short_circuit_on_first_denial"] = ShortCircuitOnFirstDenial.Value ? "true" : "false";
 251        }
 252
 52253        if (CapabilityScopes.Count > 0)
 254        {
 20255            metadata["endpoint.capability_scopes"] = string.Join(",", CapabilityScopes);
 256        }
 257
 52258        return new ReadOnlyDictionary<string, string>(metadata);
 259    }
 260
 261    private static ReadOnlyDictionary<string, string> CreateReducedMetadata(
 262        string operationName,
 263        IReadOnlyList<Type> policyTypes)
 264    {
 265        // Policy types are retained in Reduced mode. A host decision policy that varies its outcome by
 266        // endpoint.policy_types would otherwise stop seeing the marker under Reduced and silently fall back to
 267        // uniform behavior, which is a permissive change made by a metadata setting rather than a policy one.
 52268        var metadata = new Dictionary<string, string>(policyTypes.Count > 0 ? 2 : 1, StringComparer.Ordinal)
 52269        {
 52270            ["endpoint.operation_name"] = operationName
 52271        };
 272
 52273        if (policyTypes.Count > 0)
 274        {
 27275            metadata["endpoint.policy_types"] = JoinPolicyTypeNames(policyTypes);
 276        }
 277
 52278        return new ReadOnlyDictionary<string, string>(metadata);
 279    }
 280
 281    private static string JoinPolicyTypeNames(IReadOnlyList<Type> policyTypes)
 282    {
 54283        string[] policyTypeNames = new string[policyTypes.Count];
 284
 220285        for (int index = 0; index < policyTypeNames.Length; index++)
 286        {
 56287            Type policyType = policyTypes[index];
 56288            policyTypeNames[index] = policyType.FullName ?? policyType.Name;
 289        }
 290
 54291        return string.Join(",", policyTypeNames);
 292    }
 293
 294    private static string ResolveOperationName(Endpoint endpoint)
 295    {
 47296        return string.IsNullOrWhiteSpace(endpoint.DisplayName)
 47297            ? "aspnetcore.endpoint"
 47298            : endpoint.DisplayName.Trim();
 299    }
 300}