diff --git a/src/ReC.Application/Common/Behaviors/ValidationBehavior.cs b/src/ReC.Application/Common/Behaviors/ValidationBehavior.cs new file mode 100644 index 0000000..403e1e2 --- /dev/null +++ b/src/ReC.Application/Common/Behaviors/ValidationBehavior.cs @@ -0,0 +1,30 @@ +using FluentValidation; +using MediatR; + +namespace ReC.Application.Common.Behaviors +{ + public class ValidationBehavior(IEnumerable> validators) : IPipelineBehavior + where TRequest : notnull + { + public async Task Handle(TRequest request, RequestHandlerDelegate next, CancellationToken cancel) + { + if (validators.Any()) + { + var context = new ValidationContext(request); + + var validationResults = await Task.WhenAll( + validators.Select(v => + v.ValidateAsync(context, cancel))); + + var failures = validationResults + .SelectMany(r => r.Errors) + .Where(f => f != null) + .ToList(); + + if (failures.Count != 0) + throw new ValidationException(failures); + } + return await next(cancel); + } + } +} \ No newline at end of file diff --git a/src/ReC.Application/DependencyInjection.cs b/src/ReC.Application/DependencyInjection.cs index 6fd8cbc..fd0607a 100644 --- a/src/ReC.Application/DependencyInjection.cs +++ b/src/ReC.Application/DependencyInjection.cs @@ -1,8 +1,10 @@ -using Microsoft.Extensions.Configuration; +using MediatR; +using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; using ReC.Application.Common.Behaviors; using ReC.Application.Common.Options; using System.Reflection; +using FluentValidation; namespace ReC.Application; @@ -17,6 +19,8 @@ public static class DependencyInjection configOpt.ApplyConfigurations(services); + services.AddValidatorsFromAssembly(Assembly.GetExecutingAssembly()); + services.AddAutoMapper(cfg => { cfg.AddMaps(Assembly.GetExecutingAssembly()); @@ -27,6 +31,7 @@ public static class DependencyInjection { cfg.RegisterServicesFromAssembly(Assembly.GetExecutingAssembly()); cfg.AddOpenBehaviors([typeof(BodyQueryBehavior<,>), typeof(HeaderQueryBehavior<,>)]); + cfg.AddBehavior(typeof(IPipelineBehavior<,>), typeof(ValidationBehavior<,>)); cfg.LicenseKey = configOpt.LuckyPennySoftwareLicenseKey; }); diff --git a/src/ReC.Application/OutResults/Queries/ReadOutResQueryValidator.cs b/src/ReC.Application/OutResults/Queries/ReadOutResQueryValidator.cs new file mode 100644 index 0000000..54e7df7 --- /dev/null +++ b/src/ReC.Application/OutResults/Queries/ReadOutResQueryValidator.cs @@ -0,0 +1,13 @@ +using FluentValidation; + +namespace ReC.Application.OutResults.Queries; + +public class ReadOutResQueryValidator : AbstractValidator +{ + public ReadOutResQueryValidator() + { + RuleFor(x => x) + .Must(x => x.ActionId.HasValue || x.ProfileId.HasValue) + .WithMessage("At least one of ActionId or ProfileId must be provided."); + } +} \ No newline at end of file diff --git a/src/ReC.Application/ReC.Application.csproj b/src/ReC.Application/ReC.Application.csproj index d5f5270..b4fe501 100644 --- a/src/ReC.Application/ReC.Application.csproj +++ b/src/ReC.Application/ReC.Application.csproj @@ -11,6 +11,8 @@ + +