100 lines
3.5 KiB
C#
100 lines
3.5 KiB
C#
|
|
using Microsoft.Extensions.Logging;
|
|
using Microsoft.Extensions.Options;
|
|
using System;
|
|
using System.Collections.Generic;
|
|
using System.Linq;
|
|
using System.Threading.Tasks;
|
|
|
|
namespace StellaOps.Scanner.Surface.Validation;
|
|
|
|
internal sealed class SurfaceValidatorRunner : ISurfaceValidatorRunner
|
|
{
|
|
private readonly IReadOnlyList<ISurfaceValidator> _validators;
|
|
private readonly ILogger<SurfaceValidatorRunner> _logger;
|
|
private readonly ISurfaceValidationReporter _reporter;
|
|
private readonly SurfaceValidationOptions _options;
|
|
|
|
public SurfaceValidatorRunner(
|
|
IEnumerable<ISurfaceValidator> validators,
|
|
ILogger<SurfaceValidatorRunner> logger,
|
|
ISurfaceValidationReporter reporter,
|
|
IOptions<SurfaceValidationOptions> options)
|
|
{
|
|
_validators = validators?.ToArray() ?? Array.Empty<ISurfaceValidator>();
|
|
_logger = logger ?? throw new ArgumentNullException(nameof(logger));
|
|
_reporter = reporter ?? throw new ArgumentNullException(nameof(reporter));
|
|
_options = options?.Value ?? new SurfaceValidationOptions();
|
|
}
|
|
|
|
public async ValueTask<SurfaceValidationResult> RunAllAsync(
|
|
SurfaceValidationContext context,
|
|
CancellationToken cancellationToken = default)
|
|
{
|
|
if (context is null)
|
|
{
|
|
throw new ArgumentNullException(nameof(context));
|
|
}
|
|
|
|
if (_validators.Count == 0)
|
|
{
|
|
var success = SurfaceValidationResult.Success();
|
|
_reporter.Report(context, success);
|
|
return success;
|
|
}
|
|
|
|
var issues = new List<SurfaceValidationIssue>();
|
|
foreach (var validator in _validators)
|
|
{
|
|
cancellationToken.ThrowIfCancellationRequested();
|
|
|
|
try
|
|
{
|
|
var result = await validator.ValidateAsync(context, cancellationToken).ConfigureAwait(false);
|
|
if (!result.IsSuccess)
|
|
{
|
|
issues.AddRange(result.Issues);
|
|
|
|
if (!_options.ContinueOnError && result.Issues.Any(issue => issue.Severity == SurfaceValidationSeverity.Error))
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
_logger.LogError(ex, "Surface validator {Validator} threw an exception.", validator.GetType().FullName);
|
|
issues.Add(SurfaceValidationIssue.Error(
|
|
SurfaceValidationIssueCodes.ValidatorException,
|
|
$"Validator '{validator.GetType().FullName}' threw an exception: {ex.Message}",
|
|
"Inspect logs for stack trace."));
|
|
|
|
if (!_options.ContinueOnError)
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
var resultAggregate = issues.Count == 0
|
|
? SurfaceValidationResult.Success()
|
|
: SurfaceValidationResult.FromIssues(issues);
|
|
|
|
_reporter.Report(context, resultAggregate);
|
|
return resultAggregate;
|
|
}
|
|
|
|
public async ValueTask EnsureAsync(
|
|
SurfaceValidationContext context,
|
|
CancellationToken cancellationToken = default)
|
|
{
|
|
var result = await RunAllAsync(context, cancellationToken).ConfigureAwait(false);
|
|
if (!result.IsSuccess && _options.ThrowOnFailure)
|
|
{
|
|
throw new SurfaceValidationException(
|
|
$"Surface validation failed for component '{context.ComponentName}'.",
|
|
result.Issues);
|
|
}
|
|
}
|
|
}
|