diff --git a/CHANGELOG.md b/CHANGELOG.md index 240c251..6da7e17 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -26,28 +26,14 @@ This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.htm - **request.dispatcher:** Reset type caches when reloading assemblies - **request.validation:** Reset type caches when reloading assemblies -## [2.0.0] - 2026-05-30 - -## Breaking changes - -To have a consistent experience across all packages, some public interfaces have been renamed. - -### Changed - -- **request.dispatcher:** Hide pipeline internals in stack frames -- **request.validation:** Rename `IValidatorBuilder` to `IRequestValidatorBuilder` incl. extensions methods - ## [Unreleased] ### Added -- **request.validation:** Support `PropertyPath` JSON converter for string values and dictionary property names - ### Changed ### Removed [1.0.0]: https://code.geekeey.de/geekeey/request/releases/tag/1.0.0 [1.1.0]: https://code.geekeey.de/geekeey/request/releases/tag/1.1.0 -[2.0.0]: https://code.geekeey.de/geekeey/request/releases/tag/2.0.0 -[Unreleased]: https://code.geekeey.de/geekeey/request/compare/2.0.0...HEAD +[Unreleased]: https://code.geekeey.de/geekeey/request/compare/1.1.0...HEAD diff --git a/Directory.Build.props b/Directory.Build.props index 86d2794..63c71c4 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -9,8 +9,7 @@ - 2.1.0 - preview + 1.1.0 diff --git a/src/request.dispatcher.tests/StackTraceTests.cs b/src/request.dispatcher.tests/StackTraceTests.cs deleted file mode 100644 index 636dfb6..0000000 --- a/src/request.dispatcher.tests/StackTraceTests.cs +++ /dev/null @@ -1,73 +0,0 @@ -// Copyright (c) The Geekeey Authors -// SPDX-License-Identifier: EUPL-1.2 - -using Microsoft.Extensions.DependencyInjection; - -namespace Geekeey.Request.Dispatcher.Tests; - -internal sealed class StackTraceTests -{ - [Test] - public async Task I_can_not_see_pipeline_internals_are_hidden_from_stack_trace_scalar() - { - // Arrange - var sc = new ServiceCollection(); - sc.AddSingleton(); - sc.AddRequestDispatcher(builder => builder - .Add(typeof(FailingScalarHandler)) - .Add(typeof(ScalarOpenBehavior<,>))); - - var provider = sc.BuildServiceProvider(); - var dispatcher = provider.GetRequiredService(); - var request = new FailingScalarRequest(); - - // Act - var exception = await Assert.ThrowsAsync(() => - dispatcher.DispatchAsync(request)); - - // Assert - await Assert.That(exception).IsNotNull(); - - var stackTrace = await Assert.That(exception.StackTrace).IsNotNull(); - - await Assert.That(stackTrace).Contains(nameof(FailingScalarHandler)); - await Assert.That(stackTrace).Contains(nameof(ScalarOpenBehavior<,>)); - - // 3. Verify that the internal lambda from ScalarRequestInvoker.Chain is HIDDEN. - // In C#, these lambdas usually appear as "ScalarRequestInvoker`2.<>c__DisplayClass..." or similar. - // Since we added [StackTraceHidden], this frame should be omitted. - await Assert.That(stackTrace).DoesNotContain("ScalarRequestInvoker+<>"); - } - - [Test] - public async Task I_can_not_see_pipeline_internals_are_hidden_from_stack_trace_stream() - { - // Arrange - var sc = new ServiceCollection(); - sc.AddSingleton(); - sc.AddRequestDispatcher(builder => builder - .Add(typeof(FailingStreamHandler)) - .Add(typeof(StreamOpenBehavior<,>))); - - var provider = sc.BuildServiceProvider(); - var dispatcher = provider.GetRequiredService(); - var request = new FailingStreamRequest(); - - // Act - var exception = await Assert.ThrowsAsync(() => - dispatcher.DispatchAsync(request).ToListAsync().AsTask()); - - // Assert - await Assert.That(exception).IsNotNull(); - - var stackTrace = await Assert.That(exception.StackTrace).IsNotNull(); - - await Assert.That(stackTrace).Contains(nameof(FailingStreamHandler)); - await Assert.That(stackTrace).Contains(nameof(StreamOpenBehavior<,>)); - - // 3. Verify that the internal lambda from ScalarRequestInvoker.Chain is HIDDEN. - // In C#, these lambdas usually appear as "ScalarRequestInvoker`2.<>c__DisplayClass..." or similar. - // Since we added [StackTraceHidden], this frame should be omitted. - await Assert.That(stackTrace).DoesNotContain("StreamRequestInvoker+<>"); - } -} diff --git a/src/request.dispatcher.tests/_fixtures/StreamOpenBehavior.cs b/src/request.dispatcher.tests/_fixtures/StreamOpenBehavior.cs index d5ded39..3d90be3 100644 --- a/src/request.dispatcher.tests/_fixtures/StreamOpenBehavior.cs +++ b/src/request.dispatcher.tests/_fixtures/StreamOpenBehavior.cs @@ -1,19 +1,14 @@ // Copyright (c) The Geekeey Authors // SPDX-License-Identifier: EUPL-1.2 -using System.Runtime.CompilerServices; - namespace Geekeey.Request.Dispatcher.Tests; public class StreamOpenBehavior(StreamTestTracker tracker) : IStreamRequestBehavior where TRequest : IStreamRequest { - public async IAsyncEnumerable HandleAsync(TRequest request, StreamHandlerDelegate next, [EnumeratorCancellation] CancellationToken cancellationToken) + public IAsyncEnumerable HandleAsync(TRequest request, StreamHandlerDelegate next, CancellationToken cancellationToken) { tracker.Executed = true; - await foreach (var response in next(request, cancellationToken)) - { - yield return response; - } + return next(request, cancellationToken); } } diff --git a/src/request.dispatcher/ScalarRequestInvoker.cs b/src/request.dispatcher/ScalarRequestInvoker.cs index 63de03e..feb02bf 100644 --- a/src/request.dispatcher/ScalarRequestInvoker.cs +++ b/src/request.dispatcher/ScalarRequestInvoker.cs @@ -1,8 +1,6 @@ // Copyright (c) The Geekeey Authors // SPDX-License-Identifier: EUPL-1.2 -using System.Diagnostics; - using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Options; @@ -38,7 +36,7 @@ internal sealed class ScalarRequestInvoker : ScalarRequestI static ScalarHandlerDelegate Chain(ScalarHandlerDelegate next, IScalarRequestBehavior filter) { - return [StackTraceHidden] (req, ct) => filter.HandleAsync((TRequest)req, next, ct); + return (req, ct) => filter.HandleAsync((TRequest)req, next, ct); } Task Head(IScalarRequest r, CancellationToken ct) diff --git a/src/request.dispatcher/StreamRequestInvoker.cs b/src/request.dispatcher/StreamRequestInvoker.cs index 882d538..6557146 100644 --- a/src/request.dispatcher/StreamRequestInvoker.cs +++ b/src/request.dispatcher/StreamRequestInvoker.cs @@ -1,7 +1,6 @@ // Copyright (c) The Geekeey Authors // SPDX-License-Identifier: EUPL-1.2 -using System.Diagnostics; using System.Runtime.CompilerServices; using Microsoft.Extensions.DependencyInjection; @@ -42,7 +41,7 @@ internal sealed class StreamRequestInvoker : StreamRequestI static StreamHandlerDelegate Chain(StreamHandlerDelegate next, IStreamRequestBehavior filter) { - return [StackTraceHidden] (req, ct) => filter.HandleAsync((TRequest)req, next, ct); + return (req, ct) => filter.HandleAsync((TRequest)req, next, ct); } IAsyncEnumerable Head(IStreamRequest r, CancellationToken ct) diff --git a/src/request.validation.tests/DependencyInjectionTests.cs b/src/request.validation.tests/DependencyInjectionTests.cs index ae98a7a..d3cf625 100644 --- a/src/request.validation.tests/DependencyInjectionTests.cs +++ b/src/request.validation.tests/DependencyInjectionTests.cs @@ -11,7 +11,7 @@ internal sealed class DependencyInjectionTests public async Task I_can_resolve_single_validator() { var services = new ServiceCollection(); - services.AddRequestValidation(builder => + services.AddValidation(builder => { builder.Add(ServiceLifetime.Transient); }); @@ -27,7 +27,7 @@ internal sealed class DependencyInjectionTests public async Task I_can_resolve_multiple_validators() { var services = new ServiceCollection(); - services.AddRequestValidation(builder => + services.AddValidation(builder => { builder.Add(ServiceLifetime.Transient); builder.Add(ServiceLifetime.Transient); @@ -46,7 +46,7 @@ internal sealed class DependencyInjectionTests public async Task I_can_resolve_open_generic_validator() { var services = new ServiceCollection(); - services.AddRequestValidation(builder => + services.AddValidation(builder => { builder.Add(typeof(GenericValidator<>), ServiceLifetime.Transient); }); @@ -63,7 +63,7 @@ internal sealed class DependencyInjectionTests public async Task I_can_resolve_multi_interface_validator() { var services = new ServiceCollection(); - services.AddRequestValidation(builder => + services.AddValidation(builder => { builder.Add(ServiceLifetime.Transient); }); @@ -84,7 +84,7 @@ internal sealed class DependencyInjectionTests public async Task I_can_resolve_generic_validator_with_constraints() { var services = new ServiceCollection(); - services.AddRequestValidation(builder => + services.AddValidation(builder => { builder.Add(typeof(ConstrainedValidator<>), ServiceLifetime.Transient); }); @@ -104,7 +104,7 @@ internal sealed class DependencyInjectionTests public async Task I_can_resolve_generic_wrapper_validator_with_constraints() { var services = new ServiceCollection(); - services.AddRequestValidation(builder => + services.AddValidation(builder => { builder.Add(typeof(ConstrainedWrapperValidator<>), ServiceLifetime.Transient); }); @@ -124,7 +124,7 @@ internal sealed class DependencyInjectionTests public async Task I_can_resolve_aggregate_validator_directly() { var services = new ServiceCollection(); - services.AddRequestValidation(builder => + services.AddValidation(builder => { builder.Add(ServiceLifetime.Transient); builder.Add(ServiceLifetime.Transient); @@ -142,7 +142,7 @@ internal sealed class DependencyInjectionTests public async Task I_can_select_validators_from_base_classes_and_interfaces() { var services = new ServiceCollection(); - services.AddRequestValidation(builder => + services.AddValidation(builder => { builder.Add(ServiceLifetime.Transient); builder.Add(ServiceLifetime.Transient); @@ -161,7 +161,7 @@ internal sealed class DependencyInjectionTests public async Task I_can_select_validators_polymorphically_based_on_the_instance_type() { var services = new ServiceCollection(); - services.AddRequestValidation(builder => + services.AddValidation(builder => { builder.Add(ServiceLifetime.Transient); builder.Add(ServiceLifetime.Transient); diff --git a/src/request.validation.tests/PropertyPathTests.cs b/src/request.validation.tests/PropertyPathTests.cs index 2551450..bce4614 100644 --- a/src/request.validation.tests/PropertyPathTests.cs +++ b/src/request.validation.tests/PropertyPathTests.cs @@ -12,19 +12,6 @@ internal sealed class PropertyPathTests PropertyNamingPolicy = JsonNamingPolicy.CamelCase, }; - private static readonly JsonSerializerOptions CamelCaseDictionaryKeyJsonOptions = new() - { - DictionaryKeyPolicy = JsonNamingPolicy.CamelCase, - }; - - [Test] - public async Task I_can_serialize_property_paths_as_json_strings_using_the_json_naming_policy() - { - var json = JsonSerializer.Serialize((PropertyPath)"Address.Street", CamelCaseJsonOptions); - - await Assert.That(json).IsEqualTo(/*lang=json,strict*/ "\"address.street\""); - } - [Test] public async Task I_can_serialize_property_paths_using_the_json_naming_policy() { @@ -67,32 +54,6 @@ internal sealed class PropertyPathTests await Assert.That(json).IsEqualTo(/*lang=json,strict*/ """{"propertyPath":"matrix[1][2].value","severity":0,"message":"Value is required.","code":null,"attemptedValue":null}"""); } - [Test] - public async Task I_can_serialize_property_paths_as_dictionary_keys_using_the_dictionary_key_policy() - { - Dictionary errors = new() - { - ["Address.Street"] = "Street is required.", - }; - - var json = JsonSerializer.Serialize(errors, CamelCaseDictionaryKeyJsonOptions); - - await Assert.That(json).IsEqualTo(/*lang=json,strict*/ """{"address.street":"Street is required."}"""); - } - - [Test] - public async Task I_can_deserialize_property_paths_from_dictionary_keys() - { - var json = /*lang=json,strict*/ """{"Address.Street":"Street is required."}"""; - - var errors = JsonSerializer.Deserialize>(json); - - await Assert.That(errors).IsNotNull(); - await Assert.That(errors!.Count).IsEqualTo(1); - await Assert.That(errors.ContainsKey("Address.Street")).IsTrue(); - await Assert.That(errors["Address.Street"]).IsEqualTo("Street is required."); - } - [Test] public async Task I_can_iterate_and_index_segments() { diff --git a/src/request.validation/DispatchingValidator.cs b/src/request.validation/DispatchingValidator.cs index 3c3db7f..4fe7bff 100644 --- a/src/request.validation/DispatchingValidator.cs +++ b/src/request.validation/DispatchingValidator.cs @@ -4,11 +4,14 @@ using System.Collections.Concurrent; using System.Reflection.Metadata; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Options; + [assembly: MetadataUpdateHandler(typeof(Geekeey.Request.Validation.DispatchingValidator))] namespace Geekeey.Request.Validation; -internal sealed partial class DispatchingValidator : IValidator +internal sealed class DispatchingValidator : IValidator { private static readonly ConcurrentDictionary ValidatorsHandlers = new(); @@ -41,4 +44,31 @@ internal sealed partial class DispatchingValidator : IValidator return handler.Validate(context, _serviceProvider); } + + private abstract class ValidatorInvoker + { + public abstract Validation Validate(ValidationContext context, IServiceProvider serviceProvider); + } + + private sealed class ValidatorInvoker : ValidatorInvoker + { + public override Validation Validate(ValidationContext context, IServiceProvider serviceProvider) + { + var options = serviceProvider.GetRequiredService>().Value; + + var validators = options.GetValidators(serviceProvider); + + var problems = new List(); + + foreach (var validator in validators) + { + if (validator.Validate(context) is { IsValid: false, Problems: { } result }) + { + problems.AddRange(result); + } + } + + return new Validation(problems); + } + } } diff --git a/src/request.validation/IRequestValidatorBuilder.cs b/src/request.validation/IValidatorBuilder.cs similarity index 90% rename from src/request.validation/IRequestValidatorBuilder.cs rename to src/request.validation/IValidatorBuilder.cs index f699fbc..17d01ca 100644 --- a/src/request.validation/IRequestValidatorBuilder.cs +++ b/src/request.validation/IValidatorBuilder.cs @@ -8,7 +8,7 @@ namespace Geekeey.Request.Validation; /// /// Defines a builder for configuring validator registrations. /// -public interface IRequestValidatorBuilder +public interface IValidatorBuilder { /// /// Gets the service collection where the validators are registered. diff --git a/src/request.validation/PropertyPath.cs b/src/request.validation/PropertyPath.cs index 4b9bd50..4bf0e62 100644 --- a/src/request.validation/PropertyPath.cs +++ b/src/request.validation/PropertyPath.cs @@ -399,26 +399,9 @@ internal sealed class PropertyPathJsonConverter : JsonConverter throw new JsonException($"Expected {nameof(JsonTokenType.String)} but got {reader.TokenType}."); } - /// - public override PropertyPath ReadAsPropertyName(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) - { - if (reader.TokenType is JsonTokenType.PropertyName) - { - return new PropertyPath(reader.GetString()!); - } - - throw new JsonException($"Expected {nameof(JsonTokenType.PropertyName)} but got {reader.TokenType}."); - } - /// public override void Write(Utf8JsonWriter writer, PropertyPath value, JsonSerializerOptions options) { writer.WriteStringValue(value.ToJsonName(options.PropertyNamingPolicy)); } - - /// - public override void WriteAsPropertyName(Utf8JsonWriter writer, PropertyPath value, JsonSerializerOptions options) - { - writer.WritePropertyName(value.ToJsonName(options.DictionaryKeyPolicy)); - } } diff --git a/src/request.validation/ServiceCollectionExtensions.cs b/src/request.validation/ServiceCollectionExtensions.cs index 52b47bf..8574f4f 100644 --- a/src/request.validation/ServiceCollectionExtensions.cs +++ b/src/request.validation/ServiceCollectionExtensions.cs @@ -14,15 +14,15 @@ public static class ServiceCollectionExtensions /// Adds validator services to the specified . /// /// The service collection to which the validator services will be added. - /// An instance of to configure the validator registrations. - public static IRequestValidatorBuilder AddRequestValidation(this IServiceCollection services) + /// An instance of to configure the validator registrations. + public static IValidatorBuilder AddValidation(this IServiceCollection services) { ArgumentNullException.ThrowIfNull(services); - services.AddOptions(); + services.AddOptions(); services.AddTransient(); - return new RequestValidatorBuilder(services); + return new ValidatorBuilder(services); } /// @@ -32,17 +32,17 @@ public static class ServiceCollectionExtensions /// The service collection to which the validator services will be added. /// A delegate to configure the validator builder. /// The service collection with the validator services added. - public static IServiceCollection AddRequestValidation(this IServiceCollection services, Action configure) + public static IServiceCollection AddValidation(this IServiceCollection services, Action configure) { ArgumentNullException.ThrowIfNull(services); ArgumentNullException.ThrowIfNull(configure); - configure(services.AddRequestValidation()); + configure(services.AddValidation()); return services; } - private sealed class RequestValidatorBuilder(IServiceCollection services) : IRequestValidatorBuilder + private sealed class ValidatorBuilder(IServiceCollection services) : IValidatorBuilder { public IServiceCollection Services { get; } = services; } diff --git a/src/request.validation/RequestValidatorOptions.cs b/src/request.validation/ValidationOptions.cs similarity index 97% rename from src/request.validation/RequestValidatorOptions.cs rename to src/request.validation/ValidationOptions.cs index d7c5d75..b541e86 100644 --- a/src/request.validation/RequestValidatorOptions.cs +++ b/src/request.validation/ValidationOptions.cs @@ -8,12 +8,12 @@ using Microsoft.Extensions.DependencyInjection; namespace Geekeey.Request.Validation; -internal sealed class RequestValidatorOptions +internal sealed class ValidationOptions { private readonly List _search = []; private readonly Lazy _validatorsTypeIndex; - public RequestValidatorOptions() + public ValidationOptions() { _validatorsTypeIndex = new Lazy(() => new ValidatorTypeIndex(_search.Distinct())); } diff --git a/src/request.validation/RequestValidatorBuilderExtensions.cs b/src/request.validation/ValidatorBuilderExtensions.cs similarity index 59% rename from src/request.validation/RequestValidatorBuilderExtensions.cs rename to src/request.validation/ValidatorBuilderExtensions.cs index 054da7c..b71c728 100644 --- a/src/request.validation/RequestValidatorBuilderExtensions.cs +++ b/src/request.validation/ValidatorBuilderExtensions.cs @@ -6,24 +6,24 @@ using System.Reflection; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection.Extensions; -using static Geekeey.Request.Validation.RequestValidatorOptions; +using static Geekeey.Request.Validation.ValidationOptions; namespace Geekeey.Request.Validation; /// -/// Provides extension methods for configuring +/// Provides extension methods for configuring /// with additional capabilities such as searching and registering validators in assemblies or adding types directly. /// -public static class RequestValidatorBuilderExtensions +public static class ValidatorBuilderExtensions { /// /// Searches for validator types within the specified assembly and adds them to the validator /// configuration. /// - /// The to configure. + /// The to configure. /// The assembly to search for validator types. - /// The instance for further configuration. - public static IRequestValidatorBuilder SearchInAssembly(this IRequestValidatorBuilder builder, Assembly assembly) + /// The instance for further configuration. + public static IValidatorBuilder SearchInAssembly(this IValidatorBuilder builder, Assembly assembly) { ArgumentNullException.ThrowIfNull(builder); @@ -40,11 +40,11 @@ public static class RequestValidatorBuilderExtensions /// Searches for validator types within the specified assembly and adds them to the validator /// configuration with the given service lifetime. /// - /// The to configure. + /// The to configure. /// The assembly to search for validator types. /// The lifetime with which the validators are registered in the dependency injection container. - /// The instance for further configuration. - public static IRequestValidatorBuilder SearchInAssembly(this IRequestValidatorBuilder builder, Assembly assembly, ServiceLifetime lifetime) + /// The instance for further configuration. + public static IValidatorBuilder SearchInAssembly(this IValidatorBuilder builder, Assembly assembly, ServiceLifetime lifetime) { ArgumentNullException.ThrowIfNull(builder); @@ -60,14 +60,14 @@ public static class RequestValidatorBuilderExtensions /// /// Adds the specified type to the validator configuration for inspection. /// - /// The to configure. + /// The to configure. /// The type to be added to the validator configuration. - /// The instance for further configuration. - public static IRequestValidatorBuilder Add(this IRequestValidatorBuilder builder, Type type) + /// The instance for further configuration. + public static IValidatorBuilder Add(this IValidatorBuilder builder, Type type) { ArgumentNullException.ThrowIfNull(builder); - builder.Services.AddOptions() + builder.Services.AddOptions() .Configure(options => options.Inspect([type])); return builder; @@ -78,15 +78,15 @@ public static class RequestValidatorBuilderExtensions /// This also adds the type to the service collection with the specified lifetime, /// allowing it to be resolved as a dependency. /// - /// The used to configure the validators. + /// The used to configure the validators. /// The type to be added to the validator configuration. /// The lifetime scope of the type in the service container. - /// The instance for further configuration. - public static IRequestValidatorBuilder Add(this IRequestValidatorBuilder builder, Type type, ServiceLifetime lifetime) + /// The instance for further configuration. + public static IValidatorBuilder Add(this IValidatorBuilder builder, Type type, ServiceLifetime lifetime) { ArgumentNullException.ThrowIfNull(builder); - builder.Services.AddOptions() + builder.Services.AddOptions() .Configure(options => options.Inspect([type])); builder.Services.Add(new ServiceDescriptor(type, type, lifetime)); @@ -97,16 +97,16 @@ public static class RequestValidatorBuilderExtensions /// /// Adds the specified collection of types to the validator configuration for inspection. /// - /// The to configure. + /// The to configure. /// The collection of types to be added to the validator configuration. - /// The instance for further configuration. - public static IRequestValidatorBuilder Add(this IRequestValidatorBuilder builder, IEnumerable types) + /// The instance for further configuration. + public static IValidatorBuilder Add(this IValidatorBuilder builder, IEnumerable types) { ArgumentNullException.ThrowIfNull(builder); var typeList = types.ToList(); - builder.Services.AddOptions() + builder.Services.AddOptions() .Configure(options => options.Inspect(typeList)); return builder; @@ -117,17 +117,17 @@ public static class RequestValidatorBuilderExtensions /// This also adds the specified collection of types to the service collection with the specified lifetime, /// allowing it to be resolved as a dependency. /// - /// The to configure. + /// The to configure. /// The collection of types to be added to the validator configuration. /// The lifetime scope of the types in the service container. - /// The instance for further configuration. - public static IRequestValidatorBuilder Add(this IRequestValidatorBuilder builder, IEnumerable types, ServiceLifetime lifetime) + /// The instance for further configuration. + public static IValidatorBuilder Add(this IValidatorBuilder builder, IEnumerable types, ServiceLifetime lifetime) { ArgumentNullException.ThrowIfNull(builder); var typeList = types.ToList(); - builder.Services.AddOptions() + builder.Services.AddOptions() .Configure(options => options.Inspect(typeList)); builder.Services.Add(typeList.Select(export => new ServiceDescriptor(export, export, lifetime))); @@ -139,9 +139,9 @@ public static class RequestValidatorBuilderExtensions /// Adds the specified validator type to the validator configuration. /// /// The type of the validator to add. - /// The to configure. - /// The instance for further configuration. - public static IRequestValidatorBuilder Add(this IRequestValidatorBuilder builder) + /// The to configure. + /// The instance for further configuration. + public static IValidatorBuilder Add(this IValidatorBuilder builder) where TValidator : class, IValidator { return builder.Add(typeof(TValidator)); @@ -151,10 +151,10 @@ public static class RequestValidatorBuilderExtensions /// Adds the specified validator type to the validator configuration with the specified lifetime. /// /// The type of the validator to add. - /// The to configure. + /// The to configure. /// The lifetime scope of the validator in the service container. - /// The instance for further configuration. - public static IRequestValidatorBuilder Add(this IRequestValidatorBuilder builder, ServiceLifetime lifetime) + /// The instance for further configuration. + public static IValidatorBuilder Add(this IValidatorBuilder builder, ServiceLifetime lifetime) where TValidator : class, IValidator { return builder.Add(typeof(TValidator), lifetime); diff --git a/src/request.validation/ValidatorInvoker.cs b/src/request.validation/ValidatorInvoker.cs deleted file mode 100644 index ad41030..0000000 --- a/src/request.validation/ValidatorInvoker.cs +++ /dev/null @@ -1,34 +0,0 @@ -// Copyright (c) The Geekeey Authors -// SPDX-License-Identifier: EUPL-1.2 - -using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.Options; - -namespace Geekeey.Request.Validation; - -internal abstract class ValidatorInvoker -{ - public abstract Validation Validate(ValidationContext context, IServiceProvider serviceProvider); -} - -internal sealed class ValidatorInvoker : ValidatorInvoker -{ - public override Validation Validate(ValidationContext context, IServiceProvider serviceProvider) - { - var options = serviceProvider.GetRequiredService>().Value; - - var validators = options.GetValidators(serviceProvider); - - var problems = new List(); - - foreach (var validator in validators) - { - if (validator.Validate(context) is { IsValid: false, Problems: { } result }) - { - problems.AddRange(result); - } - } - - return new Validation(problems); - } -}