|
- namespace Campaign_Tracker.Server.ExtensionData;
-
- /// <summary>
- /// Implements <see cref="ILegacyLinkIntegrityCheck"/> by scanning every record supplied
- /// by the registered <see cref="ILegacyLinkedRecordProvider"/> implementations and
- /// validating each one's legacy link through <see cref="ILegacyLinkValidator"/> (AC #4).
- /// When no providers are registered (i.e. no extension records exist yet), the report
- /// reflects zero records and 100% consistency.
- /// </summary>
- public sealed class LegacyLinkIntegrityService : ILegacyLinkIntegrityCheck
- {
- private readonly IEnumerable<ILegacyLinkedRecordProvider> _providers;
- private readonly ILegacyLinkValidator _validator;
- private readonly TimeProvider _timeProvider;
-
- public LegacyLinkIntegrityService(
- IEnumerable<ILegacyLinkedRecordProvider> providers,
- ILegacyLinkValidator validator,
- TimeProvider timeProvider)
- {
- _providers = providers;
- _validator = validator;
- _timeProvider = timeProvider;
- }
-
- public async Task<LegacyLinkIntegrityReport> CheckAsync(CancellationToken cancellationToken = default)
- {
- var failures = new List<LegacyLinkIntegrityFailure>();
- var seenLinks = new Dictionary<string, ILegacyLinkedRecord>(StringComparer.OrdinalIgnoreCase);
- var providers = _providers.ToArray();
- int total = 0;
-
- foreach (var provider in providers)
- {
- var records = await provider.GetAllAsync(cancellationToken);
- foreach (var record in records)
- {
- total++;
- var result = await _validator.ValidateAsync(record.LegacyLink, cancellationToken);
- if (!result.IsValid)
- {
- failures.Add(new LegacyLinkIntegrityFailure(
- record.RecordType,
- record.RecordId,
- record.LegacyLink,
- result.Error ?? "Unknown validation error"));
- }
-
- var linkKey = $"{record.LegacyLink.Type}:{record.LegacyLink.Value}";
- if (seenLinks.TryGetValue(linkKey, out var existing) &&
- (!string.Equals(existing.RecordType, record.RecordType, StringComparison.OrdinalIgnoreCase) ||
- !string.Equals(existing.RecordId, record.RecordId, StringComparison.OrdinalIgnoreCase)))
- {
- failures.Add(new LegacyLinkIntegrityFailure(
- record.RecordType,
- record.RecordId,
- record.LegacyLink,
- $"Legacy reference is also linked by {existing.RecordType} '{existing.RecordId}', creating an ambiguous active-record join."));
- }
- else
- {
- seenLinks[linkKey] = record;
- }
- }
- }
-
- int consistent = total - failures.Count;
- double pct = total == 0 ? 100.0 : (consistent / (double)total) * 100.0;
-
- return new LegacyLinkIntegrityReport(
- CheckedAt: _timeProvider.GetUtcNow(),
- ProviderCount: providers.Length,
- TotalRecords: total,
- ConsistentRecords: consistent,
- FailedRecords: failures.Count,
- ConsistencyPercentage: Math.Round(pct, 4),
- Failures: failures);
- }
- }
|