diff --git a/EnvelopeGenerator.Application/Common/Behaviors/ValidationBehavior.cs b/EnvelopeGenerator.Application/Common/Behaviors/ValidationBehavior.cs new file mode 100644 index 00000000..8752d045 --- /dev/null +++ b/EnvelopeGenerator.Application/Common/Behaviors/ValidationBehavior.cs @@ -0,0 +1,40 @@ +using DigitalData.Core.Exceptions; +using FluentValidation; +using MediatR; + +namespace EnvelopeGenerator.Application.Common.Behaviors; + +public class ValidationBehavior : IPipelineBehavior + where TRequest : notnull +{ + private readonly IEnumerable> _validators; + + public ValidationBehavior(IEnumerable> validators) + { + _validators = validators; + } + + public async Task Handle( + TRequest request, + RequestHandlerDelegate next, + CancellationToken cancellationToken) + { + if (_validators.Any()) + { + var context = new ValidationContext(request); + var failures = _validators + .Select(v => v.Validate(context)) + .SelectMany(r => r.Errors) + .Where(f => f is not null) + .ToList(); + + if (failures.Count > 0) + { + var message = string.Join("; ", failures.Select(f => f.ErrorMessage).Distinct()); + throw new BadRequestException(message); + } + } + + return await next(); + } +} diff --git a/EnvelopeGenerator.Application/DependencyInjection.cs b/EnvelopeGenerator.Application/DependencyInjection.cs index bf41d504..97c9757c 100644 --- a/EnvelopeGenerator.Application/DependencyInjection.cs +++ b/EnvelopeGenerator.Application/DependencyInjection.cs @@ -10,6 +10,8 @@ using System.Reflection; using MediatR; using EnvelopeGenerator.Application.DocReceiverElements.Commands; using EnvelopeGenerator.Application.DocReceiverElements.Behaviors; +using EnvelopeGenerator.Application.Common.Behaviors; +using FluentValidation; namespace EnvelopeGenerator.Application; @@ -59,6 +61,9 @@ public static class DependencyInjection services.AddMediatR(cfg => { cfg.RegisterServicesFromAssembly(Assembly.GetExecutingAssembly()); + + // Generic request validation + cfg.AddOpenBehavior(typeof(ValidationBehavior<,>)); // Register SignCommand pipeline behaviors in execution order // 0. EnvelopeReceiverResolutionBehavior - Resolves EnvelopeReceiver from query parameters (executes FIRST) @@ -80,6 +85,8 @@ public static class DependencyInjection cfg.AddBehavior, SendSignedMailBehavior>(); }); + services.AddValidatorsFromAssembly(Assembly.GetExecutingAssembly()); + return services; } } diff --git a/EnvelopeGenerator.Application/Documents/Queries/ReadDocumentDetailsQueryValidator.cs b/EnvelopeGenerator.Application/Documents/Queries/ReadDocumentDetailsQueryValidator.cs new file mode 100644 index 00000000..b9c88872 --- /dev/null +++ b/EnvelopeGenerator.Application/Documents/Queries/ReadDocumentDetailsQueryValidator.cs @@ -0,0 +1,13 @@ +using FluentValidation; + +namespace EnvelopeGenerator.Application.Documents.Queries; + +public class ReadDocumentDetailsQueryValidator : AbstractValidator +{ + public ReadDocumentDetailsQueryValidator() + { + RuleFor(x => x.EnvelopeId) + .GreaterThan(0) + .WithMessage("EnvelopeId is required."); + } +} diff --git a/EnvelopeGenerator.Application/Documents/Queries/ReadDocumentQueryValidator.cs b/EnvelopeGenerator.Application/Documents/Queries/ReadDocumentQueryValidator.cs new file mode 100644 index 00000000..251d49eb --- /dev/null +++ b/EnvelopeGenerator.Application/Documents/Queries/ReadDocumentQueryValidator.cs @@ -0,0 +1,23 @@ +using FluentValidation; + +namespace EnvelopeGenerator.Application.Documents.Queries; + +public class ReadDocumentQueryValidator : AbstractValidator +{ + public ReadDocumentQueryValidator() + { + RuleFor(x => x) + .Must(x => (x.Id ?? 0) > 0 || (x.EnvelopeId ?? 0) > 0) + .WithMessage("Id or EnvelopeId is required."); + + RuleFor(x => x.Id) + .GreaterThan(0) + .When(x => x.Id.HasValue) + .WithMessage("Id must be greater than 0."); + + RuleFor(x => x.EnvelopeId) + .GreaterThan(0) + .When(x => x.EnvelopeId.HasValue) + .WithMessage("EnvelopeId must be greater than 0."); + } +} diff --git a/EnvelopeGenerator.Application/EnvelopeGenerator.Application.csproj b/EnvelopeGenerator.Application/EnvelopeGenerator.Application.csproj index 5ea118b7..b1b676c5 100644 --- a/EnvelopeGenerator.Application/EnvelopeGenerator.Application.csproj +++ b/EnvelopeGenerator.Application/EnvelopeGenerator.Application.csproj @@ -19,6 +19,8 @@ + +