< Summary

Information
Class: AmbientServices.StatusResultsBuilder
Assembly: AmbientServices
File(s): /home/runner/work/AmbientServices/AmbientServices/AmbientServices/Status/StatusResultsBuilder.cs
Tag: 332_35464845198
Line coverage
100%
Covered lines: 80
Uncovered lines: 0
Coverable lines: 80
Total lines: 325
Line coverage: 100%
Branch coverage
95%
Covered branches: 44
Total branches: 46
Branch coverage: 95.6%
Method coverage

Feature is only available for sponsors

Upgrade to PRO version

Metrics

MethodBranch coverage Crap Score Cyclomatic complexity Line coverage
.ctor(...)100%11100%
.ctor(...)100%66100%
.ctor(...)100%11100%
.ctor(...)100%11100%
get_Elapsed()100%11100%
get_FinalResults()83.33%1212100%
AddProperty(...)100%11100%
AddProperty<T>(...)100%11100%
FindProperty(...)100%11100%
AddChild(...)100%11100%
AddChild(...)100%11100%
AddChild(...)100%11100%
AddException(...)100%66100%
AddFailure(...)100%66100%
AddAlert(...)100%88100%
AddOkay(...)100%66100%
AddSuperlative(...)100%22100%
CreateRawStatusResultsBuilder(...)100%11100%
CreateRawStatusResults(...)100%11100%

File(s)

/home/runner/work/AmbientServices/AmbientServices/AmbientServices/Status/StatusResultsBuilder.cs

#LineLine coverage
 1using AmbientServices.Extensions;
 2using System;
 3using System.Collections.Generic;
 4using System.Linq;
 5using System.Web;
 6
 7namespace AmbientServices;
 8
 9/// <summary>
 10/// A mutable class that can make gathering data for a <see cref="StatusResults"/> object easier.
 11/// </summary>
 12/// <remarks>
 13/// <pitch>
 14/// The mutable scratchpad an <see cref="StatusAuditor.Audit"/> implementation fills in: add properties, child nodes, an
 15/// </pitch>
 16/// <pledge>
 17/// Worst-alert-wins: the alert recording methods (<see cref="AddException"/>, <see cref="AddFailure"/>, <see cref="AddA
 18/// <see cref="FinalResults"/> obeys the <see cref="StatusResults"/> report-or-children invariant: if any alert was reco
 19/// </pledge>
 20/// <plan>
 21/// A thin mutable shell: property and child lists, settable node metadata, and a running worst-alert comparison on <see
 22/// </plan>
 23/// <pin><see cref="StatusResults"/></pin>
 24/// </remarks>
 25public class StatusResultsBuilder
 26{
 27
 228    private readonly List<StatusProperty> _properties = new();
 229    private readonly List<StatusResultsBuilder> _children = new();
 30
 31    //private int? _hiddenFailuresTolerated;
 32    //private float? _spatialDistributionOfRedundancy;
 33
 34
 35    /// <summary>
 36    /// Constructs an empty StatusResultsBuilder, ready to fill with properties, children, and alerts.
 37    /// </summary>
 38    /// <param name="targetSystem">The target system (if any apply at this level).</param>
 239    public StatusResultsBuilder(string targetSystem)
 40    {
 241        TargetSystem = targetSystem;
 242        AuditStartTime = AmbientClock.UtcNow;
 243        RelativeDetailLevel = 1;
 244        NatureOfSystem = StatusNatureOfSystem.ChildrenHeterogeneous;
 245    }
 46    /// <summary>
 47    /// Constructs a StatusResultsBuilder from the specified <see cref="StatusResults"/>.
 48    /// </summary>
 49    /// <param name="results">The <see cref="StatusResults"/> to copy from.</param>
 250    public StatusResultsBuilder(StatusResults results)
 51    {
 52#if NET5_0_OR_GREATER
 253        ArgumentNullException.ThrowIfNull(results);
 54#else
 55        if (results is null) throw new ArgumentNullException(nameof(results));
 56#endif
 257        SourceSystem = results.SourceSystem;
 258        TargetSystem = results.TargetSystem;
 259        AuditStartTime = results.Time;
 260        RelativeDetailLevel = results.RelativeDetailLevel;
 261        NatureOfSystem = results.NatureOfSystem;
 262        WorstAlert = results.Report?.Alert;
 263        NextAuditTime = results.Report?.NextAuditTime;
 264        _properties = new List<StatusProperty>(results.Properties);
 265        _children = new List<StatusResultsBuilder>(results.Children.Select(c => new StatusResultsBuilder(c)));
 266    }
 67    /// <summary>
 68    /// Constructs an empty StatusResultsBuilder, ready to fill with properties, children, and alerts.
 69    /// <see cref="NatureOfSystem"/> start with a value of <see cref="StatusNatureOfSystem.ChildrenHeterogeneous"/>.
 70    /// </summary>
 71    /// <param name="checker">The <see cref="StatusChecker"/> we are going to build results for.</param>
 272    public StatusResultsBuilder(StatusChecker checker)
 73    {
 74#if NET5_0_OR_GREATER
 275        ArgumentNullException.ThrowIfNull(checker);
 76#else
 77        if (checker is null) throw new ArgumentNullException(nameof(checker));
 78#endif
 279        TargetSystem = checker.TargetSystem;
 280        AuditStartTime = AmbientClock.UtcNow;
 281        RelativeDetailLevel = 1;
 282        NatureOfSystem = StatusNatureOfSystem.ChildrenHeterogeneous;
 283    }
 84    /// <summary>
 85    /// Constructs a StatusResultsBuilder with a set of baseline properties.
 86    /// <see cref="NatureOfSystem"/> start with a value of <see cref="StatusNatureOfSystem.ChildrenHeterogeneous"/>.
 87    /// </summary>
 88    /// <param name="checker">The <see cref="StatusChecker"/> we are going to build results for.</param>
 89    /// <param name="baselineProperties">An enumeration of baseline <see cref="StatusProperty"/>s to initialize the prop
 90    public StatusResultsBuilder(StatusChecker checker, IEnumerable<StatusProperty> baselineProperties)
 291        : this(checker)
 92    {
 293        _properties.AddRange(baselineProperties);
 294    }
 95    /// <summary>
 96    /// Gets or sets the source system name, which identifies the system doing the measurement.
 97    /// </summary>
 98    /// <remarks>
 99    /// Defaults to null, which indicates that the measurement was done on the local system.
 100    /// The source system name can be used to summarize multiple results from different sources for the same target.
 101    /// When results are gathered from a remote system, a node will be inserted indicating the system the results origin
 102    /// Only the last (closest) source system name in the tree will be used.
 103    /// </remarks>
 104    public string? SourceSystem { get; set; }
 105    /// <summary>
 106    /// Gets the target system name, which identifies either a top-level system or the part of a system being audited or
 107    /// The target system may only be specified explicitly in the constructor, or implicilty through the <see cref="Stat
 108    /// </summary>
 109    /// <remarks>
 110    /// Target system names are concatenated with ancestor and descendant nodes and used to aggregate errors from the sa
 111    /// Targets with a leading slash character indicate that the system is a shared system and may have status results m
 112    /// Shared targets are not concatenated to the targets indicated by ancestor nodes, and their parents are ignored du
 113    /// Defaults to null, but should almost always be set to a non-empty string.
 114    /// Null should only be used to indicate that this node is not related to any specific target system, which would pr
 115    /// </remarks>
 116    public string? TargetSystem { get; }
 117    /// <summary>
 118    /// Gets or sets the audit start time for this node.  Defaults to the time the constructor was called.
 119    /// </summary>
 120    public DateTime AuditStartTime { get; set; }
 121    /// <summary>
 122    /// Gets or sets the audit duration for this node.  If not set, will use the difference between <see cref="AuditStar
 123    /// </summary>
 124    public TimeSpan? AuditDuration { get; set; }
 125    /// <summary>
 126    /// Gets or sets the relative detail level for this node, with zero meaning that data from this node should always b
 127    /// Nodes may be filtered based on detail level.
 128    /// Defaults to one.
 129    /// </summary>
 130    public int RelativeDetailLevel { get; set; }
 131    /// <summary>
 132    /// Gets or sets the <see cref="StatusNatureOfSystem"/> value indicating what type of node this is relative to its c
 133    /// Defaults to <see cref="StatusNatureOfSystem.ChildrenHeterogeneous"/>.
 134    /// </summary>
 135    public StatusNatureOfSystem NatureOfSystem { get; set; }
 136    /// <summary>
 137    /// Gets or sets the optional <see cref="DateTime"/> indicating when the next audit will happen (if any).
 138    /// Defaults to null.
 139    /// </summary>
 140    public DateTime? NextAuditTime { get; set; }
 141    /// <summary>
 142    /// Gets the worst rated <see cref="StatusAuditAlert"/> that has been reported so far.
 143    /// </summary>
 144    public StatusAuditAlert? WorstAlert { get; private set; }
 145    /// <summary>
 146    /// Gets the elapsed time since the audit start time.
 147    /// </summary>
 2148    public TimeSpan Elapsed => AmbientClock.UtcNow - AuditStartTime;
 149
 150    /// <summary>
 151    /// Gets the final <see cref="StatusResults"/> generated from the results builder.
 152    /// </summary>
 153    public StatusResults FinalResults
 154    {
 155        get
 156        {
 2157            TimeSpan duration = (AuditDuration == null) ? (AmbientClock.UtcNow - AuditStartTime) : AuditDuration.Value;
 2158            StatusAuditReport? report = WorstAlert is null ? null : new StatusAuditReport(AuditStartTime, duration, Next
 2159            return (report == null)
 2160                ? new StatusResults(SourceSystem, TargetSystem ?? "", AuditStartTime, RelativeDetailLevel, _properties, 
 2161                : new StatusResults(SourceSystem, TargetSystem ?? "", AuditStartTime, RelativeDetailLevel, _properties, 
 162        }
 163    }
 164    /// <summary>
 165    /// Adds the specified property.
 166    /// </summary>
 167    /// <param name="name">The name for the property.</param>
 168    /// <param name="value">The value for the property.</param>
 169    public void AddProperty(string name, string value)
 170    {
 2171        _properties.Add(new StatusProperty(name, value));
 2172    }
 173    /// <summary>
 174    /// Adds the specified property.
 175    /// </summary>
 176    /// <param name="name">The name for the property.</param>
 177    /// <param name="value">The value for the propertiy, for which <see cref="object.ToString()"/> will be called to con
 178    public void AddProperty<T>(string name, T value) where T : notnull
 179    {
 2180        _properties.Add(StatusProperty.Create(name, value));
 2181    }
 182    /// <summary>
 183    /// Finds a property with the specified name, if possible.
 184    /// </summary>
 185    /// <param name="name">The name of the property to look for.</param>
 186    /// <returns>The <see cref="StatusProperty"/> that was found, or null if no status property with that name exists in
 187    public StatusProperty? FindProperty(string name)
 188    {
 2189        return _properties.Find(a => string.Equals(a.Name, name, StringComparison.Ordinal));
 190    }
 191    /// <summary>
 192    /// Adds the specified <see cref="StatusResultsBuilder"/> as a child to the node we're building.
 193    /// </summary>
 194    /// <param name="child">The child <see cref="StatusResultsBuilder"/>.</param>
 195    public void AddChild(StatusResultsBuilder child)
 196    {
 2197        _children.Add(child);
 2198    }
 199    /// <summary>
 200    /// Adds the specified <see cref="StatusResults"/> as a child to the node we're building.
 201    /// </summary>
 202    /// <param name="child">The child <see cref="StatusResults"/>.</param>
 203    public void AddChild(StatusResults child)
 204    {
 2205        _children.Add(new StatusResultsBuilder(child));
 2206    }
 207    /// <summary>
 208    /// Adds a child node with the specified name and returns the corresponding <see cref="StatusResultsBuilder"/>.
 209    /// </summary>
 210    /// <param name="childName">The name of the child node to add.</param>
 211    public StatusResultsBuilder AddChild(string childName)
 212    {
 2213        StatusResultsBuilder childNode = new(childName);
 2214        _children.Add(childNode);
 2215        return childNode;
 216    }
 217    /// <summary>
 218    /// Records an exception alert.
 219    /// </summary>
 220    /// <param name="severity">A positive number greater than or equal to 0.0 indicating the relative severity of the fa
 221    /// <param name="ex">The <see cref="Exception"/> that occurred.</param>
 222    public void AddException(Exception ex, float severity = 0.5f)
 223    {
 2224        if (severity < 0.0) throw new ArgumentOutOfRangeException(nameof(severity), "The specified severity must be grea
 2225        float rating = StatusRating.Fail - severity;
 2226        string exceptionType = ex.TypeName();
 2227        string exceptionTerse = "[" + exceptionType + "] " + ex.Message.Replace(Environment.NewLine, Environment.NewLine
 2228        string exceptionDetails = HttpUtility.HtmlEncode(ex.ToFilteredString().Trim()).Replace(Environment.NewLine, "<br
 2229        if (WorstAlert is null || rating < WorstAlert.Rating)
 230        {
 2231            WorstAlert = new StatusAuditAlert(rating, exceptionType, exceptionTerse, exceptionDetails);
 232        }
 2233    }
 234    /// <summary>
 235    /// Records a failure alert.
 236    /// </summary>
 237    /// <param name="auditAlertCode">An audit alert code which can be used to identify this type of error across multipl
 238    /// <param name="terse">A terse string (suitable for SMS) indicating the nature of the failure.</param>
 239    /// <param name="details">A detailed string indicating the nature of the failure.  Should use the same formatting as
 240    /// <param name="severity">A positive number greater than or equal to 0.0 indicating the relative severity of the fa
 241    public void AddFailure(string auditAlertCode, string terse, string details, float severity = 0.5f)
 242    {
 2243        if (severity < 0.0) throw new ArgumentOutOfRangeException(nameof(severity), "The specified severity must be grea
 2244        float rating = StatusRating.Fail - severity;
 2245        if (WorstAlert is null || rating < WorstAlert.Rating)
 246        {
 2247            WorstAlert = new StatusAuditAlert(rating, auditAlertCode, terse, details);
 248        }
 2249    }
 250    /// <summary>
 251    /// Records a normal alert.
 252    /// </summary>
 253    /// <param name="auditAlertCode">An audit alert code which can be used to identify this type of alert across multipl
 254    /// <param name="terse">A terse string (suitable for SMS) indicating the nature of the alert.</param>
 255    /// <param name="details">A detailed string indicating the nature of the alert.  Should use the same formatting as d
 256    /// <param name="severity">A positive number between 0.0 (inclusive) and 1.0 (exclusive) indicating the relative sev
 257    public void AddAlert(string auditAlertCode, string terse, string details, float severity = 0.5f)
 258    {
 2259        if (severity < 0.0 || severity >= 1.0) throw new ArgumentOutOfRangeException(nameof(severity), "The specified se
 2260        float rating = StatusRating.Alert - severity;
 2261        if (WorstAlert is null || rating < WorstAlert.Rating)
 262        {
 2263            WorstAlert = new StatusAuditAlert(rating, auditAlertCode, terse, details);
 264        }
 2265    }
 266    /// <summary>
 267    /// Records an okay alert.
 268    /// </summary>
 269    /// <param name="auditAlertCode">An audit alert code which can be used to identify this type of alert across multipl
 270    /// <param name="terse">A terse string (suitable for SMS) indicating the nature of the issue.</param>
 271    /// <param name="details">A detailed string indicating the nature of the issue.  Should use the same formatting as d
 272    /// <param name="severity">A positive number between 0.0 (inclusive) and 1.0 (exclusive) indicating the relative sev
 273    public void AddOkay(string auditAlertCode, string terse, string details, float severity = 0.5f)
 274    {
 2275        if (severity >= 1.0) throw new ArgumentOutOfRangeException(nameof(severity), "The specified severity must less t
 2276        float rating = StatusRating.Okay - severity;
 2277        if (WorstAlert is null || rating < WorstAlert.Rating)
 278        {
 2279            WorstAlert = new StatusAuditAlert(rating, auditAlertCode, terse, details);
 280        }
 2281    }
 282    /// <summary>
 283    /// Records a superlative alert.
 284    /// </summary>
 285    /// <param name="auditAlertCode">An audit alert code which can be used to identify this type of alert across multipl
 286    /// <param name="terse">A terse string (suitable for SMS) indicating the nature of the issue.</param>
 287    /// <param name="details">A detailed string indicating the nature of the issue.  Should use the same formatting as d
 288    public void AddSuperlative(string auditAlertCode, string terse, string details)
 289    {
 2290        float rating = StatusRating.Superlative;
 2291        if (WorstAlert is null)
 292        {
 2293            WorstAlert = new StatusAuditAlert(rating, auditAlertCode, terse, details);
 294        }
 2295    }
 296    /// <summary>
 297    /// Constructs a leaf node <see cref="StatusResultsBuilder"/> directly from the information for a single alert.
 298    /// </summary>
 299    /// <param name="targetSystem">The name of the target system.</param>
 300    /// <param name="rating">The status rating.</param>
 301    /// <param name="auditAlertCode">The audit alert code.</param>
 302    /// <param name="terse">The terse message.</param>
 303    /// <param name="details">The detailed message.</param>
 304    /// <returns>A <see cref="StatusResultsBuilder"/> constructed from the specified parameters.</returns>
 305    public static StatusResultsBuilder CreateRawStatusResultsBuilder(string targetSystem, float rating, string auditAler
 306    {
 2307        StatusResultsBuilder temp = new(targetSystem);
 2308        temp.NatureOfSystem = StatusNatureOfSystem.Leaf;
 2309        temp.WorstAlert = new StatusAuditAlert(rating, auditAlertCode, terse, details);
 2310        return temp;
 311    }
 312    /// <summary>
 313    /// Constructs a leaf node <see cref="StatusResults"/> directly from the information for a single alert.
 314    /// </summary>
 315    /// <param name="targetSystem">The name of the target system.</param>
 316    /// <param name="rating">The status rating.</param>
 317    /// <param name="auditAlertCode">The audit alert code.</param>
 318    /// <param name="terse">The terse message.</param>
 319    /// <param name="details">The detailed message.</param>
 320    /// <returns>A <see cref="StatusResults"/> constructed from the specified parameters.</returns>
 321    public static StatusResults CreateRawStatusResults(string targetSystem, float rating, string auditAlertCode, string 
 322    {
 2323        return CreateRawStatusResultsBuilder(targetSystem, rating, auditAlertCode, terse, details).FinalResults;
 324    }
 325}