using FluentValidation;
using FluentValidation.Results;
using Microsoft.Extensions.Options;
namespace Vegasco.WebApi.Common;
public static class ValidatorExtensions
{
///
/// Asynchronously validates an instance of against all instances in .
///
///
///
///
/// The failed validation results.
public static async Task> ValidateAllAsync(this IEnumerable> validators, T instance)
{
var validationTasks = validators
.Select(validator => validator.ValidateAsync(instance))
.ToList();
await Task.WhenAll(validationTasks);
List failedValidations = validationTasks
.Select(x => x.Result)
.Where(x => !x.IsValid)
.ToList();
return failedValidations;
}
public static Dictionary ToCombinedDictionary(this IEnumerable validationResults)
{
// Use a hash set to avoid duplicate error messages.
Dictionary> combinedErrors = [];
foreach (var error in validationResults.SelectMany(x => x.Errors))
{
if (!combinedErrors.TryGetValue(error.PropertyName, out HashSet? value))
{
value = ([error.ErrorMessage]);
combinedErrors[error.PropertyName] = value;
continue;
}
value.Add(error.ErrorMessage);
}
return combinedErrors.ToDictionary(x => x.Key, x => x.Value.ToArray());
}
public static OptionsBuilder ValidateFluently(this OptionsBuilder builder)
where T : class
{
builder.Services.AddTransient>(serviceProvider =>
{
var validators = serviceProvider.GetServices>() ?? [];
return new FluentValidationOptions(builder.Name, validators);
});
return builder;
}
}
internal class FluentValidationOptions : IValidateOptions
where TOptions : class
{
private readonly IEnumerable> _validators;
public string? Name { get; set; }
public FluentValidationOptions(string? name, IEnumerable> validators)
{
Name = name;
_validators = validators;
}
public ValidateOptionsResult Validate(string? name, TOptions options)
{
if (name is not null && name != Name)
{
return ValidateOptionsResult.Skip;
}
ArgumentNullException.ThrowIfNull(options);
var failedValidations = _validators.ValidateAllAsync(options).Result;
if (failedValidations.Count == 0)
{
return ValidateOptionsResult.Success;
}
return ValidateOptionsResult.Fail(failedValidations.SelectMany(x => x.Errors.Select(x => x.ErrorMessage)));
}
}