< Summary - ZhEaIsNsAaBn.Libraries coverage (affected: 23 packages)

Information
Class: ZhEaIsNsAaBn.Utilities.APIResponses.APIResponsesSettings
Assembly: ZhEaIsNsAaBn.Utilities.APIResponses
File(s): /home/runner/work/ZhEaIsNsAaBn.Libraries.V1/ZhEaIsNsAaBn.Libraries.V1/src/UtilitiesPackages/ZhEaIsNsAaBn.Utilities.APIResponses/APIResponsesSettings.cs
Line coverage
100%
Covered lines: 35
Uncovered lines: 0
Coverable lines: 35
Total lines: 102
Line coverage: 100%
Branch coverage
78%
Covered branches: 11
Total branches: 14
Branch coverage: 78.5%
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%
MapCode(...)100%11100%
MapCategory(...)100%11100%
StatusFor(...)75%88100%
Find(...)83.33%66100%

File(s)

/home/runner/work/ZhEaIsNsAaBn.Libraries.V1/ZhEaIsNsAaBn.Libraries.V1/src/UtilitiesPackages/ZhEaIsNsAaBn.Utilities.APIResponses/APIResponsesSettings.cs

#LineLine coverage
 1using System.Collections.Frozen;
 2
 3namespace ZhEaIsNsAaBn.Utilities.APIResponses;
 4
 5/// <summary>
 6/// How a failed response's HTTP status is chosen from its first error: the error <see cref="Error.Code"/> (app mapping,
 7/// the defaults), then its category, <see cref="Error.Type"/> (app mapping, then the defaults), then <see cref="Fallbac
 8/// Register with <c>services.AddAPIResponses( s =&gt; … )</c>; without it the defaults apply.
 9/// </summary>
 10/// <remarks>
 11/// Defaults — codes: <c>UnAuthorized</c> 401, <c>Forbidden</c> 403, <c>NotFound</c> 404, <c>UniqueConstraintViolation</
 12/// <c>UniqueConstraint</c> / <c>ConcurrencyException</c> 409, <c>Unknown</c> 500, <c>BadGateway</c> 502, <c>GatewayTime
 13/// <c>DatabaseError</c> / <c>Unknown</c> / <c>Exception</c> 500. Fallback 400. Codes and categories ignore case.
 14/// </remarks>
 15public sealed class APIResponsesSettings
 16{
 1017    private static readonly FrozenDictionary<string, HttpStatusCode> _defaultCodes = new Dictionary<string, HttpStatusCo
 1018                                                                                         {
 1019                                                                                             [ "UnAuthorized" ]         
 1020                                                                                             [ "Forbidden" ]            
 1021                                                                                             [ "NotFound" ]             
 1022                                                                                             [ "UniqueConstraintViolatio
 1023                                                                                             [ "UniqueConstraint" ]     
 1024                                                                                             [ "ConcurrencyException" ] 
 1025                                                                                             [ "Unknown" ]              
 1026                                                                                             [ "BadGateway" ]           
 1027                                                                                             [ "GatewayTimeout" ]       
 1028                                                                                         }.ToFrozenDictionary( StringCom
 29
 1030    private static readonly FrozenDictionary<string, HttpStatusCode> _defaultCategories = new Dictionary<string, HttpSta
 1031                                                                                              {
 1032                                                                                                  [ "Validation" ]    = 
 1033                                                                                                  [ "Business" ]      = 
 1034                                                                                                  [ "DatabaseError" ] = 
 1035                                                                                                  [ "Unknown" ]       = 
 1036                                                                                                  [ "Exception" ]     = 
 1037                                                                                              }.ToFrozenDictionary( Stri
 38
 45139    private readonly Dictionary<string, HttpStatusCode> _categories = new( StringComparer.OrdinalIgnoreCase );
 45140    private readonly Dictionary<string, HttpStatusCode> _codes      = new( StringComparer.OrdinalIgnoreCase );
 41
 42
 43    /// <summary>The status when neither the code nor the category is mapped. Default: 400.</summary>
 44    public HttpStatusCode Fallback { get; set; } = HttpStatusCode.BadRequest;
 45
 46
 47    /// <summary>Maps an error code to a status; it wins over every default and every category.</summary>
 48    /// <param name="code">The <see cref="Error.Code"/>, e.g. <c>NotFound</c>.</param>
 49    /// <param name="status">The HTTP status.</param>
 50    /// <returns>These settings.</returns>
 51    /// <exception cref="ArgumentException"><paramref name="code"/> is empty.</exception>
 52    public APIResponsesSettings MapCode( string code, HttpStatusCode status )
 53    {
 1854        ArgumentException.ThrowIfNullOrWhiteSpace( code );
 55
 1856        _codes[ code ] = status;
 57
 1858        return this;
 59    }
 60
 61    /// <summary>Maps an error category (<see cref="Error.Type"/>) to a status; used when no code mapping (app or defaul
 62    /// <param name="category">The <see cref="Error.Type"/>, e.g. <c>DatabaseError</c>.</param>
 63    /// <param name="status">The HTTP status.</param>
 64    /// <returns>These settings.</returns>
 65    /// <exception cref="ArgumentException"><paramref name="category"/> is empty.</exception>
 66    public APIResponsesSettings MapCategory( string category, HttpStatusCode status )
 67    {
 1468        ArgumentException.ThrowIfNullOrWhiteSpace( category );
 69
 1470        _categories[ category ] = status;
 71
 1472        return this;
 73    }
 74
 75    /// <summary>The status for a failed response with <paramref name="errors"/>, from the first real error.</summary>
 76    /// <param name="errors">The errors; <see cref="Error.None"/> entries are skipped.</param>
 77    /// <returns>The status; <see cref="Fallback"/> when there is no real error.</returns>
 78    public HttpStatusCode StatusFor( IEnumerable<Error>? errors )
 79    {
 43780        var error = errors?.FirstOrDefault( e => e is not null && e != Error.None );
 81
 43782        if ( error is null ) { return Fallback; }
 83
 84
 71785        if ( Find( _codes, _defaultCodes, error.Code ) is { } byCode ) { return byCode; }
 86
 87
 15788        return Find( _categories, _defaultCategories, error.Type ) ?? Fallback;
 89    }
 90
 91
 92    private static HttpStatusCode? Find( Dictionary<string, HttpStatusCode> app, FrozenDictionary<string, HttpStatusCode
 93    {
 59494        if ( string.IsNullOrEmpty( key ) ) { return null; }
 95
 96
 62397        if ( app.TryGetValue( key, out var mapped ) ) { return mapped; }
 98
 99
 565100        return defaults.TryGetValue( key, out var fallback ) ? fallback : null;
 101    }
 102}