Files
git.stella-ops.org/src/StellaOps.Concelier.Models/AdvisoryProvenance.cs
2025-10-18 20:46:16 +03:00

71 lines
2.2 KiB
C#

using System.Collections.Immutable;
using System.Linq;
using System.Text.Json.Serialization;
namespace StellaOps.Concelier.Models;
/// <summary>
/// Describes the origin of a canonical field and how/when it was captured.
/// </summary>
public sealed record AdvisoryProvenance
{
public static AdvisoryProvenance Empty { get; } = new("unknown", "unspecified", string.Empty, DateTimeOffset.UnixEpoch);
[JsonConstructor]
public AdvisoryProvenance(
string source,
string kind,
string value,
string? decisionReason,
DateTimeOffset recordedAt,
ImmutableArray<string> fieldMask)
: this(source, kind, value, recordedAt, fieldMask.IsDefault ? null : fieldMask.AsEnumerable(), decisionReason)
{
}
public AdvisoryProvenance(
string source,
string kind,
string value,
DateTimeOffset recordedAt,
IEnumerable<string>? fieldMask = null,
string? decisionReason = null)
{
Source = Validation.EnsureNotNullOrWhiteSpace(source, nameof(source));
Kind = Validation.EnsureNotNullOrWhiteSpace(kind, nameof(kind));
Value = Validation.TrimToNull(value);
DecisionReason = Validation.TrimToNull(decisionReason);
RecordedAt = recordedAt.ToUniversalTime();
FieldMask = NormalizeFieldMask(fieldMask);
}
public string Source { get; }
public string Kind { get; }
public string? Value { get; }
public string? DecisionReason { get; }
public DateTimeOffset RecordedAt { get; }
public ImmutableArray<string> FieldMask { get; }
private static ImmutableArray<string> NormalizeFieldMask(IEnumerable<string>? fieldMask)
{
if (fieldMask is null)
{
return ImmutableArray<string>.Empty;
}
var buffer = fieldMask
.Where(static value => !string.IsNullOrWhiteSpace(value))
.Select(static value => value.Trim().ToLowerInvariant())
.Distinct(StringComparer.Ordinal)
.OrderBy(static value => value, StringComparer.Ordinal)
.ToImmutableArray();
return buffer.IsDefault ? ImmutableArray<string>.Empty : buffer;
}
}