< Summary

Information
Class: AsiBackbone.AspNetCore.Results.AsiBackboneHttpResultMappingExtensions
Assembly: AsiBackbone.AspNetCore
File(s): /home/runner/work/AsiBackbone/AsiBackbone/src/AsiBackbone.AspNetCore/Results/AsiBackboneHttpResultMappingExtensions.cs
Line coverage
95%
Covered lines: 88
Uncovered lines: 4
Coverable lines: 92
Total lines: 242
Line coverage: 95.6%
Branch coverage
88%
Covered branches: 39
Total branches: 44
Branch coverage: 88.6%
Method coverage

Feature is only available for sponsors

Upgrade to PRO version

Metrics

MethodBranch coverage Crap Score Cyclomatic complexity Line coverage
ToHttpResult(...)100%11100%
ToHttpResult(...)100%22100%
ToHttpResult(...)100%11100%
ToHttpResult(...)100%22100%
ResolveDecisionStatusCode(...)85.71%7790%
CreateDecisionProblemDetails(...)100%11100%
CreateOperationProblemDetails(...)100%11100%
CreateDecisionBody(...)100%11100%
CreateOperationBody(...)100%11100%
ResolveDecisionTitle(...)57.14%8770%
AddDecisionExtensions(...)100%11100%
AddOperationExtensions(...)100%11100%
AddDecisionPayload(...)100%1818100%
AddOperationPayload(...)87.5%88100%

File(s)

/home/runner/work/AsiBackbone/AsiBackbone/src/AsiBackbone.AspNetCore/Results/AsiBackboneHttpResultMappingExtensions.cs

#LineLine coverage
 1using AsiBackbone.Core.Decisions;
 2using AsiBackbone.Core.Results;
 3using Microsoft.AspNetCore.Http;
 4using Microsoft.AspNetCore.Mvc;
 5
 6namespace AsiBackbone.AspNetCore.Results;
 7
 8/// <summary>
 9/// Provides helpers for translating AsiBackbone Core decisions and operation results into ASP.NET Core HTTP results.
 10/// </summary>
 11public static class AsiBackboneHttpResultMappingExtensions
 12{
 13    private const string OutcomeExtensionName = "outcome";
 14    private const string ReasonCodesExtensionName = "reasonCodes";
 15    private const string ReasonMessagesExtensionName = "reasonMessages";
 16    private const string CorrelationIdExtensionName = "correlationId";
 17    private const string TraceIdExtensionName = "traceId";
 18    private const string PolicyVersionExtensionName = "policyVersion";
 19    private const string PolicyHashExtensionName = "policyHash";
 20
 21    /// <summary>
 22    /// Maps a governance decision into an ASP.NET Core result using default safe HTTP mapping options.
 23    /// </summary>
 24    /// <param name="decision">The governance decision to map.</param>
 25    /// <returns>An ASP.NET Core result.</returns>
 26    public static IResult ToHttpResult(this GovernanceDecision decision)
 27    {
 1228        return decision.ToHttpResult(new AsiBackboneHttpResultMappingOptions());
 29    }
 30
 31    /// <summary>
 32    /// Maps a governance decision into an ASP.NET Core result using host-provided HTTP mapping options.
 33    /// </summary>
 34    /// <param name="decision">The governance decision to map.</param>
 35    /// <param name="options">The mapping options.</param>
 36    /// <returns>An ASP.NET Core result.</returns>
 37    public static IResult ToHttpResult(this GovernanceDecision decision, AsiBackboneHttpResultMappingOptions options)
 38    {
 2039        ArgumentNullException.ThrowIfNull(decision);
 1840        ArgumentNullException.ThrowIfNull(options);
 1641        options.Validate();
 42
 1643        return decision.CanProceed
 1644            ? Microsoft.AspNetCore.Http.Results.Json(CreateDecisionBody(decision, options, allowed: true), statusCode: R
 1645            : Microsoft.AspNetCore.Http.Results.Problem(CreateDecisionProblemDetails(decision, options));
 46    }
 47
 48    /// <summary>
 49    /// Maps an operation result into an ASP.NET Core result using default safe HTTP mapping options.
 50    /// </summary>
 51    /// <param name="result">The operation result to map.</param>
 52    /// <returns>An ASP.NET Core result.</returns>
 53    public static IResult ToHttpResult(this OperationResult result)
 54    {
 855        return result.ToHttpResult(new AsiBackboneHttpResultMappingOptions());
 56    }
 57
 58    /// <summary>
 59    /// Maps an operation result into an ASP.NET Core result using host-provided HTTP mapping options.
 60    /// </summary>
 61    /// <param name="result">The operation result to map.</param>
 62    /// <param name="options">The mapping options.</param>
 63    /// <returns>An ASP.NET Core result.</returns>
 64    public static IResult ToHttpResult(this OperationResult result, AsiBackboneHttpResultMappingOptions options)
 65    {
 1266        ArgumentNullException.ThrowIfNull(result);
 1067        ArgumentNullException.ThrowIfNull(options);
 868        options.Validate();
 69
 870        return result.Succeeded
 871            ? Microsoft.AspNetCore.Http.Results.Json(CreateOperationBody(result, options), statusCode: options.SuccessSt
 872            : Microsoft.AspNetCore.Http.Results.Problem(CreateOperationProblemDetails(result, options));
 73    }
 74
 75    private static int ResolveDecisionStatusCode(GovernanceDecision decision, AsiBackboneHttpResultMappingOptions option
 76    {
 1677        return decision.Outcome switch
 1678        {
 479            GovernanceDecisionOutcome.Allowed => options.SuccessStatusCode,
 280            GovernanceDecisionOutcome.Warning => options.WarningStatusCode,
 481            GovernanceDecisionOutcome.Denied => options.DeniedStatusCode,
 282            GovernanceDecisionOutcome.Deferred => options.DeferredStatusCode,
 283            GovernanceDecisionOutcome.AcknowledgmentRequired => options.AcknowledgmentRequiredStatusCode,
 284            GovernanceDecisionOutcome.EscalationRecommended => options.EscalationRecommendedStatusCode,
 085            _ => options.OperationFailureStatusCode,
 1686        };
 87    }
 88
 89    private static ProblemDetails CreateDecisionProblemDetails(
 90        GovernanceDecision decision,
 91        AsiBackboneHttpResultMappingOptions options)
 92    {
 1093        int statusCode = ResolveDecisionStatusCode(decision, options);
 1094        ProblemDetails problemDetails = new()
 1095        {
 1096            Status = statusCode,
 1097            Title = ResolveDecisionTitle(decision.Outcome),
 1098            Detail = options.GovernanceDecisionNotAllowedMessage.Trim(),
 1099            Type = "https://tools.ietf.org/html/rfc9110",
 10100        };
 101
 10102        AddDecisionExtensions(problemDetails.Extensions, decision, options);
 103
 10104        return problemDetails;
 105    }
 106
 107    private static ProblemDetails CreateOperationProblemDetails(
 108        OperationResult result,
 109        AsiBackboneHttpResultMappingOptions options)
 110    {
 4111        ProblemDetails problemDetails = new()
 4112        {
 4113            Status = options.OperationFailureStatusCode,
 4114            Title = "Operation failed.",
 4115            Detail = options.OperationFailureMessage.Trim(),
 4116            Type = "https://tools.ietf.org/html/rfc9110",
 4117        };
 118
 4119        AddOperationExtensions(problemDetails.Extensions, result, options);
 120
 4121        return problemDetails;
 122    }
 123
 124    private static Dictionary<string, object?> CreateDecisionBody(
 125        GovernanceDecision decision,
 126        AsiBackboneHttpResultMappingOptions options,
 127        bool allowed)
 128    {
 6129        Dictionary<string, object?> body = new(StringComparer.Ordinal)
 6130        {
 6131            ["allowed"] = allowed,
 6132            [OutcomeExtensionName] = decision.Outcome.ToString(),
 6133        };
 134
 6135        AddDecisionPayload(body, decision, options);
 136
 6137        return body;
 138    }
 139
 140    private static Dictionary<string, object?> CreateOperationBody(OperationResult result, AsiBackboneHttpResultMappingO
 141    {
 4142        Dictionary<string, object?> body = new(StringComparer.Ordinal)
 4143        {
 4144            ["succeeded"] = result.Succeeded,
 4145        };
 146
 4147        AddOperationPayload(body, result, options);
 148
 4149        return body;
 150    }
 151
 152    private static string ResolveDecisionTitle(GovernanceDecisionOutcome outcome)
 153    {
 10154        return outcome switch
 10155        {
 0156            GovernanceDecisionOutcome.Allowed => "Governance decision allowed execution.",
 0157            GovernanceDecisionOutcome.Warning => "Governance decision allowed execution with warnings.",
 4158            GovernanceDecisionOutcome.Denied => "Governance decision denied execution.",
 2159            GovernanceDecisionOutcome.Deferred => "Governance decision deferred execution.",
 2160            GovernanceDecisionOutcome.AcknowledgmentRequired => "Governance decision requires acknowledgment.",
 2161            GovernanceDecisionOutcome.EscalationRecommended => "Governance decision recommends escalation.",
 0162            _ => "Governance decision did not allow immediate execution.",
 10163        };
 164    }
 165
 166    private static void AddDecisionExtensions(
 167        IDictionary<string, object?> extensions,
 168        GovernanceDecision decision,
 169        AsiBackboneHttpResultMappingOptions options)
 170    {
 10171        extensions[OutcomeExtensionName] = decision.Outcome.ToString();
 10172        AddDecisionPayload(extensions, decision, options);
 10173    }
 174
 175    private static void AddOperationExtensions(
 176        IDictionary<string, object?> extensions,
 177        OperationResult result,
 178        AsiBackboneHttpResultMappingOptions options)
 179    {
 4180        AddOperationPayload(extensions, result, options);
 4181    }
 182
 183    private static void AddDecisionPayload(
 184        IDictionary<string, object?> payload,
 185        GovernanceDecision decision,
 186        AsiBackboneHttpResultMappingOptions options)
 187    {
 16188        if (decision.ReasonCodes.Count > 0)
 189        {
 12190            payload[ReasonCodesExtensionName] = decision.ReasonCodes;
 191        }
 192
 16193        if (options.IncludeReasonMessages && decision.Reasons.Count > 0)
 194        {
 4195            payload[ReasonMessagesExtensionName] = decision.Reasons.Select(reason => reason.Message).ToArray();
 196        }
 197
 16198        if (!string.IsNullOrWhiteSpace(decision.CorrelationId))
 199        {
 6200            payload[CorrelationIdExtensionName] = decision.CorrelationId;
 201        }
 202
 16203        if (options.IncludeTraceId && !string.IsNullOrWhiteSpace(decision.TraceId))
 204        {
 2205            payload[TraceIdExtensionName] = decision.TraceId;
 206        }
 207
 16208        if (options.IncludePolicyMetadata)
 209        {
 4210            if (!string.IsNullOrWhiteSpace(decision.PolicyVersion))
 211            {
 2212                payload[PolicyVersionExtensionName] = decision.PolicyVersion;
 213            }
 214
 4215            if (!string.IsNullOrWhiteSpace(decision.PolicyHash))
 216            {
 2217                payload[PolicyHashExtensionName] = decision.PolicyHash;
 218            }
 219        }
 16220    }
 221
 222    private static void AddOperationPayload(
 223        IDictionary<string, object?> payload,
 224        OperationResult result,
 225        AsiBackboneHttpResultMappingOptions options)
 226    {
 8227        if (result.ReasonCodes.Count > 0)
 228        {
 4229            payload[ReasonCodesExtensionName] = result.ReasonCodes;
 230        }
 231
 8232        if (result.HasWarnings)
 233        {
 2234            payload["warnings"] = result.Warnings;
 235        }
 236
 8237        if (options.IncludeReasonMessages && result.Reasons.Count > 0)
 238        {
 4239            payload[ReasonMessagesExtensionName] = result.Reasons.Select(reason => reason.Message).ToArray();
 240        }
 8241    }
 242}

Methods/Properties

ToHttpResult(AsiBackbone.Core.Decisions.GovernanceDecision)
ToHttpResult(AsiBackbone.Core.Decisions.GovernanceDecision,AsiBackbone.AspNetCore.Results.AsiBackboneHttpResultMappingOptions)
ToHttpResult(AsiBackbone.Core.Results.OperationResult)
ToHttpResult(AsiBackbone.Core.Results.OperationResult,AsiBackbone.AspNetCore.Results.AsiBackboneHttpResultMappingOptions)
ResolveDecisionStatusCode(AsiBackbone.Core.Decisions.GovernanceDecision,AsiBackbone.AspNetCore.Results.AsiBackboneHttpResultMappingOptions)
CreateDecisionProblemDetails(AsiBackbone.Core.Decisions.GovernanceDecision,AsiBackbone.AspNetCore.Results.AsiBackboneHttpResultMappingOptions)
CreateOperationProblemDetails(AsiBackbone.Core.Results.OperationResult,AsiBackbone.AspNetCore.Results.AsiBackboneHttpResultMappingOptions)
CreateDecisionBody(AsiBackbone.Core.Decisions.GovernanceDecision,AsiBackbone.AspNetCore.Results.AsiBackboneHttpResultMappingOptions,System.Boolean)
CreateOperationBody(AsiBackbone.Core.Results.OperationResult,AsiBackbone.AspNetCore.Results.AsiBackboneHttpResultMappingOptions)
ResolveDecisionTitle(AsiBackbone.Core.Decisions.GovernanceDecisionOutcome)
AddDecisionExtensions(System.Collections.Generic.IDictionary`2<System.String,System.Object>,AsiBackbone.Core.Decisions.GovernanceDecision,AsiBackbone.AspNetCore.Results.AsiBackboneHttpResultMappingOptions)
AddOperationExtensions(System.Collections.Generic.IDictionary`2<System.String,System.Object>,AsiBackbone.Core.Results.OperationResult,AsiBackbone.AspNetCore.Results.AsiBackboneHttpResultMappingOptions)
AddDecisionPayload(System.Collections.Generic.IDictionary`2<System.String,System.Object>,AsiBackbone.Core.Decisions.GovernanceDecision,AsiBackbone.AspNetCore.Results.AsiBackboneHttpResultMappingOptions)
AddOperationPayload(System.Collections.Generic.IDictionary`2<System.String,System.Object>,AsiBackbone.Core.Results.OperationResult,AsiBackbone.AspNetCore.Results.AsiBackboneHttpResultMappingOptions)