diff --git a/CHANGELOG.md b/CHANGELOG.md index 9f581d1..5c26b92 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -16,16 +16,8 @@ This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.htm ### Added -- **request.validation:** Dependency injection support for validators via `IServiceCollection.AddValidation()` -- **request.validation:** `IValidatorBuilder` for fluent validator registration and assembly scanning -- **request.validation:** Support for open generic validators and automatic closing during resolution -- **request.validation:** `Validate` extension method for simplified validator invocation - ### Changed -- **request.dispatcher:** Reset type caches when reloading assemblies -- **request.validation:** Reset type caches when reloading assemblies - ### Removed [1.0.0]: https://code.geekeey.de/geekeey/request/releases/tag/1.0.0 diff --git a/src/request.dispatcher/RequestDispatcher.cs b/src/request.dispatcher/RequestDispatcher.cs index 2acb426..c4083bd 100644 --- a/src/request.dispatcher/RequestDispatcher.cs +++ b/src/request.dispatcher/RequestDispatcher.cs @@ -20,12 +20,6 @@ internal sealed class RequestDispatcher : IRequestDispatcher _serviceProvider = serviceProvider; } - public static void ClearCache(Type[]? _) - { - ScalarRequestHandlers.Clear(); - StreamRequestHandlers.Clear(); - } - public Task DispatchAsync(IScalarRequest request, CancellationToken cancellationToken = default) { ArgumentNullException.ThrowIfNull(request); diff --git a/src/request.validation.tests/DependencyInjectionTests.cs b/src/request.validation.tests/DependencyInjectionTests.cs deleted file mode 100644 index d3cf625..0000000 --- a/src/request.validation.tests/DependencyInjectionTests.cs +++ /dev/null @@ -1,177 +0,0 @@ -// Copyright (c) The Geekeey Authors -// SPDX-License-Identifier: EUPL-1.2 - -using Microsoft.Extensions.DependencyInjection; - -namespace Geekeey.Request.Validation.Tests; - -internal sealed class DependencyInjectionTests -{ - [Test] - public async Task I_can_resolve_single_validator() - { - var services = new ServiceCollection(); - services.AddValidation(builder => - { - builder.Add(ServiceLifetime.Transient); - }); - var provider = services.BuildServiceProvider(); - - var validator = provider.GetRequiredService(); - var result = validator.Validate(new StringValueModel()); - - await Assert.That(result.Problems).Count().IsEqualTo(3); - } - - [Test] - public async Task I_can_resolve_multiple_validators() - { - var services = new ServiceCollection(); - services.AddValidation(builder => - { - builder.Add(ServiceLifetime.Transient); - builder.Add(ServiceLifetime.Transient); - }); - var provider = services.BuildServiceProvider(); - - var validator = provider.GetRequiredService(); - var result = validator.Validate(new Person()); - - await Assert.That(result.Problems).Count().IsEqualTo(2); - await Assert.That(result.Problems.Any(p => p.Message == "PersonValidator failure.")).IsTrue(); - await Assert.That(result.Problems.Any(p => p.Message == "AnotherPersonValidator failure.")).IsTrue(); - } - - [Test] - public async Task I_can_resolve_open_generic_validator() - { - var services = new ServiceCollection(); - services.AddValidation(builder => - { - builder.Add(typeof(GenericValidator<>), ServiceLifetime.Transient); - }); - var provider = services.BuildServiceProvider(); - - var validator = provider.GetRequiredService(); - var result = validator.Validate(new Person()); - - await Assert.That(result.Problems).Count().IsEqualTo(1); - await Assert.That(result.Problems[0].Message).IsEqualTo("GenericValidator failure."); - } - - [Test] - public async Task I_can_resolve_multi_interface_validator() - { - var services = new ServiceCollection(); - services.AddValidation(builder => - { - builder.Add(ServiceLifetime.Transient); - }); - var provider = services.BuildServiceProvider(); - - var validator = provider.GetRequiredService(); - - var personResult = validator.Validate(new Person()); - await Assert.That(personResult.Problems).Count().IsEqualTo(1); - await Assert.That(personResult.Problems[0].Message).IsEqualTo("MultiInterfaceValidator failure."); - - var addressResult = validator.Validate(new Address()); - await Assert.That(addressResult.Problems).Count().IsEqualTo(1); - await Assert.That(addressResult.Problems[0].Message).IsEqualTo("MultiInterfaceValidator failure."); - } - - [Test] - public async Task I_can_resolve_generic_validator_with_constraints() - { - var services = new ServiceCollection(); - services.AddValidation(builder => - { - builder.Add(typeof(ConstrainedValidator<>), ServiceLifetime.Transient); - }); - var provider = services.BuildServiceProvider(); - - var validator = provider.GetRequiredService(); - - var teamResult = validator.Validate(new Team()); - await Assert.That(teamResult.Problems).Count().IsEqualTo(1); - await Assert.That(teamResult.Problems[0].Message).IsEqualTo("ConstrainedValidator failure."); - - var personResult = validator.Validate(new Person()); - await Assert.That(personResult.Problems).Count().IsEqualTo(0); - } - - [Test] - public async Task I_can_resolve_generic_wrapper_validator_with_constraints() - { - var services = new ServiceCollection(); - services.AddValidation(builder => - { - builder.Add(typeof(ConstrainedWrapperValidator<>), ServiceLifetime.Transient); - }); - var provider = services.BuildServiceProvider(); - - var validator = provider.GetRequiredService(); - - var teamWrapperResult = validator.Validate(new Wrapper()); - await Assert.That(teamWrapperResult.Problems).Count().IsEqualTo(1); - await Assert.That(teamWrapperResult.Problems[0].Message).IsEqualTo("ConstrainedWrapperValidator failure."); - - var personWrapperResult = validator.Validate(new Wrapper()); - await Assert.That(personWrapperResult.Problems).Count().IsEqualTo(0); - } - - [Test] - public async Task I_can_resolve_aggregate_validator_directly() - { - var services = new ServiceCollection(); - services.AddValidation(builder => - { - builder.Add(ServiceLifetime.Transient); - builder.Add(ServiceLifetime.Transient); - }); - var provider = services.BuildServiceProvider(); - - var validator = provider.GetRequiredService(); - - var person = new Person { Name = "John" }; - var result = validator.Validate(person); - await Assert.That(result.Problems).Count().IsEqualTo(2); - } - - [Test] - public async Task I_can_select_validators_from_base_classes_and_interfaces() - { - var services = new ServiceCollection(); - services.AddValidation(builder => - { - builder.Add(ServiceLifetime.Transient); - builder.Add(ServiceLifetime.Transient); - builder.Add(ServiceLifetime.Transient); - }); - var provider = services.BuildServiceProvider(); - - var validator = provider.GetRequiredService(); - var dog = new Dog { Id = 0, Name = "", Breed = "" }; - var result = validator.Validate(dog); - - await Assert.That(result.Problems).Count().IsEqualTo(3); - } - - [Test] - public async Task I_can_select_validators_polymorphically_based_on_the_instance_type() - { - var services = new ServiceCollection(); - services.AddValidation(builder => - { - builder.Add(ServiceLifetime.Transient); - builder.Add(ServiceLifetime.Transient); - }); - var provider = services.BuildServiceProvider(); - - var validator = provider.GetRequiredService(); - var dog = new Dog { Name = "", Breed = "" }; - var result = validator.Validate(new ValidationContext(dog)); - - await Assert.That(result.Problems).Count().IsEqualTo(2); - } -} diff --git a/src/request.validation.tests/_fixtures/AnotherPersonValidator.cs b/src/request.validation.tests/_fixtures/AnotherPersonValidator.cs deleted file mode 100644 index be49f70..0000000 --- a/src/request.validation.tests/_fixtures/AnotherPersonValidator.cs +++ /dev/null @@ -1,12 +0,0 @@ -// Copyright (c) The Geekeey Authors -// SPDX-License-Identifier: EUPL-1.2 - -namespace Geekeey.Request.Validation.Tests; - -internal sealed class AnotherPersonValidator : Validator -{ - public AnotherPersonValidator() - { - RuleFor(p => p.Name).Must(_ => false, "AnotherPersonValidator failure."); - } -} diff --git a/src/request.validation.tests/_fixtures/ConstrainedValidator.cs b/src/request.validation.tests/_fixtures/ConstrainedValidator.cs deleted file mode 100644 index 11e9833..0000000 --- a/src/request.validation.tests/_fixtures/ConstrainedValidator.cs +++ /dev/null @@ -1,12 +0,0 @@ -// Copyright (c) The Geekeey Authors -// SPDX-License-Identifier: EUPL-1.2 - -namespace Geekeey.Request.Validation.Tests; - -internal sealed class ConstrainedValidator : Validator where T : ICustomConstraint -{ - public ConstrainedValidator() - { - RuleFor(x => x).Must(_ => false, "ConstrainedValidator failure."); - } -} diff --git a/src/request.validation.tests/_fixtures/ConstrainedWrapperValidator.cs b/src/request.validation.tests/_fixtures/ConstrainedWrapperValidator.cs deleted file mode 100644 index 4dd88e2..0000000 --- a/src/request.validation.tests/_fixtures/ConstrainedWrapperValidator.cs +++ /dev/null @@ -1,12 +0,0 @@ -// Copyright (c) The Geekeey Authors -// SPDX-License-Identifier: EUPL-1.2 - -namespace Geekeey.Request.Validation.Tests; - -internal sealed class ConstrainedWrapperValidator : Validator> where T : ICustomConstraint -{ - public ConstrainedWrapperValidator() - { - RuleFor(x => x).Must(_ => false, "ConstrainedWrapperValidator failure."); - } -} diff --git a/src/request.validation.tests/_fixtures/GenericValidator.cs b/src/request.validation.tests/_fixtures/GenericValidator.cs deleted file mode 100644 index 6c9254e..0000000 --- a/src/request.validation.tests/_fixtures/GenericValidator.cs +++ /dev/null @@ -1,12 +0,0 @@ -// Copyright (c) The Geekeey Authors -// SPDX-License-Identifier: EUPL-1.2 - -namespace Geekeey.Request.Validation.Tests; - -internal sealed class GenericValidator : Validator -{ - public GenericValidator() - { - RuleFor(x => x).Must(_ => false, "GenericValidator failure."); - } -} diff --git a/src/request.validation.tests/_fixtures/ICustomConstraint.cs b/src/request.validation.tests/_fixtures/ICustomConstraint.cs deleted file mode 100644 index 4b135a6..0000000 --- a/src/request.validation.tests/_fixtures/ICustomConstraint.cs +++ /dev/null @@ -1,6 +0,0 @@ -// Copyright (c) The Geekeey Authors -// SPDX-License-Identifier: EUPL-1.2 - -namespace Geekeey.Request.Validation.Tests; - -internal interface ICustomConstraint { } diff --git a/src/request.validation.tests/_fixtures/MultiInterfaceValidator.cs b/src/request.validation.tests/_fixtures/MultiInterfaceValidator.cs deleted file mode 100644 index 5a1f732..0000000 --- a/src/request.validation.tests/_fixtures/MultiInterfaceValidator.cs +++ /dev/null @@ -1,32 +0,0 @@ -// Copyright (c) The Geekeey Authors -// SPDX-License-Identifier: EUPL-1.2 - -namespace Geekeey.Request.Validation.Tests; - -internal sealed class MultiInterfaceValidator : IValidator, IValidator
-{ - public Validation Validate(Person instance) - { - return Validate(new ValidationContext(instance)); - } - - public Validation Validate(Address instance) - { - return Validate(new ValidationContext
(instance)); - } - - public Validation Validate(ValidationContext context) - { - if (context.Instance is Person) - { - return new Validation([new Problem { PropertyPath = new PropertyPath("Person"), Message = "MultiInterfaceValidator failure." }]); - } - - if (context.Instance is Address) - { - return new Validation([new Problem { PropertyPath = new PropertyPath("Address"), Message = "MultiInterfaceValidator failure." }]); - } - - return new Validation([]); - } -} diff --git a/src/request.validation.tests/_fixtures/PersonValidator.cs b/src/request.validation.tests/_fixtures/PersonValidator.cs deleted file mode 100644 index 788fdea..0000000 --- a/src/request.validation.tests/_fixtures/PersonValidator.cs +++ /dev/null @@ -1,12 +0,0 @@ -// Copyright (c) The Geekeey Authors -// SPDX-License-Identifier: EUPL-1.2 - -namespace Geekeey.Request.Validation.Tests; - -internal sealed class PersonValidator : Validator -{ - public PersonValidator() - { - RuleFor(p => p.Name).Must(_ => false, "PersonValidator failure."); - } -} diff --git a/src/request.validation.tests/_fixtures/PolymorphicTypes.cs b/src/request.validation.tests/_fixtures/PolymorphicTypes.cs deleted file mode 100644 index fe060fa..0000000 --- a/src/request.validation.tests/_fixtures/PolymorphicTypes.cs +++ /dev/null @@ -1,44 +0,0 @@ -// Copyright (c) The Geekeey Authors -// SPDX-License-Identifier: EUPL-1.2 - -namespace Geekeey.Request.Validation.Tests; - -public interface IEntity -{ - int Id { get; set; } -} - -public abstract class Animal : IEntity -{ - public int Id { get; set; } - public string? Name { get; set; } -} - -public class Dog : Animal -{ - public string? Breed { get; set; } -} - -internal sealed class EntityValidator : Validator -{ - public EntityValidator() - { - RuleFor(x => x.Id).GreaterThan(0); - } -} - -internal sealed class AnimalValidator : Validator -{ - public AnimalValidator() - { - RuleFor(x => x.Name).NotEmpty(); - } -} - -internal sealed class DogValidator : Validator -{ - public DogValidator() - { - RuleFor(x => x.Breed).NotEmpty(); - } -} diff --git a/src/request.validation.tests/_fixtures/Team.cs b/src/request.validation.tests/_fixtures/Team.cs index ce4e7b6..6b17c01 100644 --- a/src/request.validation.tests/_fixtures/Team.cs +++ b/src/request.validation.tests/_fixtures/Team.cs @@ -3,7 +3,7 @@ namespace Geekeey.Request.Validation.Tests; -internal sealed class Team : ICustomConstraint +internal sealed class Team { public IEnumerable Members { get; init; } = []; } diff --git a/src/request.validation.tests/_fixtures/Wrapper.cs b/src/request.validation.tests/_fixtures/Wrapper.cs deleted file mode 100644 index ecb0feb..0000000 --- a/src/request.validation.tests/_fixtures/Wrapper.cs +++ /dev/null @@ -1,6 +0,0 @@ -// Copyright (c) The Geekeey Authors -// SPDX-License-Identifier: EUPL-1.2 - -namespace Geekeey.Request.Validation.Tests; - -internal sealed class Wrapper { } diff --git a/src/request.validation/DispatchingValidator.cs b/src/request.validation/DispatchingValidator.cs deleted file mode 100644 index 4fe7bff..0000000 --- a/src/request.validation/DispatchingValidator.cs +++ /dev/null @@ -1,74 +0,0 @@ -// Copyright (c) The Geekeey Authors -// SPDX-License-Identifier: EUPL-1.2 - -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 class DispatchingValidator : IValidator -{ - private static readonly ConcurrentDictionary ValidatorsHandlers = new(); - - private readonly IServiceProvider _serviceProvider; - - public DispatchingValidator(IServiceProvider serviceProvider) - { - _serviceProvider = serviceProvider; - } - - public static void ClearCache(Type[]? _) - { - ValidatorsHandlers.Clear(); - } - - public Validation Validate(ValidationContext context) - { - ArgumentNullException.ThrowIfNull(context); - - if (context.Instance is null) - { - return new Validation([]); - } - - var handler = ValidatorsHandlers.GetOrAdd(context.Instance.GetType(), static key => - { - var type = typeof(ValidatorInvoker<>).MakeGenericType(key); - return (ValidatorInvoker)Activator.CreateInstance(type)!; - }); - - 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/Geekeey.Request.Validation.csproj b/src/request.validation/Geekeey.Request.Validation.csproj index 33bea6b..098287f 100644 --- a/src/request.validation/Geekeey.Request.Validation.csproj +++ b/src/request.validation/Geekeey.Request.Validation.csproj @@ -28,9 +28,4 @@ - - - - - diff --git a/src/request.validation/IValidatorBuilder.cs b/src/request.validation/IValidatorBuilder.cs deleted file mode 100644 index 17d01ca..0000000 --- a/src/request.validation/IValidatorBuilder.cs +++ /dev/null @@ -1,17 +0,0 @@ -// Copyright (c) The Geekeey Authors -// SPDX-License-Identifier: EUPL-1.2 - -using Microsoft.Extensions.DependencyInjection; - -namespace Geekeey.Request.Validation; - -/// -/// Defines a builder for configuring validator registrations. -/// -public interface IValidatorBuilder -{ - /// - /// Gets the service collection where the validators are registered. - /// - IServiceCollection Services { get; } -} diff --git a/src/request.validation/ServiceCollectionExtensions.cs b/src/request.validation/ServiceCollectionExtensions.cs deleted file mode 100644 index 8574f4f..0000000 --- a/src/request.validation/ServiceCollectionExtensions.cs +++ /dev/null @@ -1,49 +0,0 @@ -// Copyright (c) The Geekeey Authors -// SPDX-License-Identifier: EUPL-1.2 - -using Microsoft.Extensions.DependencyInjection; - -namespace Geekeey.Request.Validation; - -/// -/// Provides extension methods for configuring and registering validator services in the . -/// -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 IValidatorBuilder AddValidation(this IServiceCollection services) - { - ArgumentNullException.ThrowIfNull(services); - - services.AddOptions(); - services.AddTransient(); - - return new ValidatorBuilder(services); - } - - /// - /// Adds validator services to the specified - /// and configures them using the provided . - /// - /// 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 AddValidation(this IServiceCollection services, Action configure) - { - ArgumentNullException.ThrowIfNull(services); - ArgumentNullException.ThrowIfNull(configure); - - configure(services.AddValidation()); - - return services; - } - - private sealed class ValidatorBuilder(IServiceCollection services) : IValidatorBuilder - { - public IServiceCollection Services { get; } = services; - } -} diff --git a/src/request.validation/ValidationOptions.cs b/src/request.validation/ValidationOptions.cs deleted file mode 100644 index b541e86..0000000 --- a/src/request.validation/ValidationOptions.cs +++ /dev/null @@ -1,145 +0,0 @@ -// Copyright (c) The Geekeey Authors -// SPDX-License-Identifier: EUPL-1.2 - -using System.Collections; -using System.Collections.Concurrent; - -using Microsoft.Extensions.DependencyInjection; - -namespace Geekeey.Request.Validation; - -internal sealed class ValidationOptions -{ - private readonly List _search = []; - private readonly Lazy _validatorsTypeIndex; - - public ValidationOptions() - { - _validatorsTypeIndex = new Lazy(() => new ValidatorTypeIndex(_search.Distinct())); - } - - public void Inspect(IEnumerable assembly) - { - if (_validatorsTypeIndex.IsValueCreated) - { - throw new InvalidOperationException("The type index has already been created. Cannot inspect new assemblies."); - } - - _search.AddRange(assembly); - } - - public IEnumerable> GetValidators(IServiceProvider services) - { - return _validatorsTypeIndex.Value.Resolve>(services); - } - - private abstract class TypeIndex - { - private readonly ConcurrentDictionary> _cache = new(); - - protected readonly Dictionary> _closedTypeInfo = []; - protected readonly List _openTypeInfo = []; - - protected TypeIndex(IEnumerable collection, Func predicate) - { - foreach (var type in collection) - { - if (type.IsGenericTypeDefinition) - { - if (type.GetInterfaces().Any(predicate)) - { - _openTypeInfo.Add(type); - } - } - else - { - foreach (var @interface in type.GetInterfaces().Where(predicate)) - { - (_closedTypeInfo.TryGetValue(@interface, out var list) ? list : _closedTypeInfo[@interface] = []).Add(type); - } - } - } - } - - public IEnumerable Resolve(IServiceProvider services) - { - return (IEnumerable)_cache.GetOrAdd(typeof(T), CreateResolverFactory)(services); - } - - protected abstract IReadOnlyList IsAssignableTo(Type type); - - private Func> CreateResolverFactory(Type @interface) - { - var list = IsAssignableTo(@interface); - return ResolverFactory; - - IEnumerable ResolverFactory(IServiceProvider services) - { - foreach (var type in list) - { - yield return (T)ActivatorUtilities.GetServiceOrCreateInstance(services, type); - } - } - } - } - - internal static bool IsValidatorType(Type type) - { - return type.IsGenericType && type.GetGenericTypeDefinition() == typeof(IValidator<>); - } - - private sealed class ValidatorTypeIndex(IEnumerable collection) - : TypeIndex(collection, IsValidatorType) - { - protected override IReadOnlyList IsAssignableTo(Type @interface) - { - var result = new List(); - - foreach (var kvp in _closedTypeInfo) - { - if (@interface.IsAssignableFrom(kvp.Key)) - { - result.AddRange(kvp.Value); - } - } - - var validatedType = @interface.GetGenericArguments()[0]; - - foreach (var type in _openTypeInfo) - { - try - { - // open type case one: Validator : IValidator - // We try to close it with the validated type. - var impl = type.MakeGenericType(validatedType); - if (impl.IsAssignableTo(@interface)) - { - result.Add(impl); - } - } - catch (ArgumentException) - { - } - - try - { - // open type case two: Validator : IValidator> - // If the validated type is generic, we try to close the validator with the validated type's generic arguments. - if (validatedType.IsGenericType) - { - var impl = type.MakeGenericType(validatedType.GetGenericArguments()); - if (impl.IsAssignableTo(@interface)) - { - result.Add(impl); - } - } - } - catch (ArgumentException) - { - } - } - - return result; - } - } -} diff --git a/src/request.validation/ValidatorBuilderExtensions.cs b/src/request.validation/ValidatorBuilderExtensions.cs deleted file mode 100644 index b71c728..0000000 --- a/src/request.validation/ValidatorBuilderExtensions.cs +++ /dev/null @@ -1,167 +0,0 @@ -// Copyright (c) The Geekeey Authors -// SPDX-License-Identifier: EUPL-1.2 - -using System.Reflection; - -using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.DependencyInjection.Extensions; - -using static Geekeey.Request.Validation.ValidationOptions; - -namespace Geekeey.Request.Validation; - -/// -/// Provides extension methods for configuring -/// with additional capabilities such as searching and registering validators in assemblies or adding types directly. -/// -public static class ValidatorBuilderExtensions -{ - /// - /// Searches for validator types within the specified assembly and adds them to the validator - /// configuration. - /// - /// The to configure. - /// The assembly to search for validator types. - /// The instance for further configuration. - public static IValidatorBuilder SearchInAssembly(this IValidatorBuilder builder, Assembly assembly) - { - ArgumentNullException.ThrowIfNull(builder); - - var exports = assembly.GetTypes() - .Where(type => type is { IsClass: true, IsAbstract: false }) - .Where(IsValidatorImplementationType); - - builder.Add(exports); - - return builder; - } - - /// - /// Searches for validator types within the specified assembly and adds them to the validator - /// configuration with the given service lifetime. - /// - /// 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 IValidatorBuilder SearchInAssembly(this IValidatorBuilder builder, Assembly assembly, ServiceLifetime lifetime) - { - ArgumentNullException.ThrowIfNull(builder); - - var exports = assembly.GetTypes() - .Where(type => type is { IsClass: true, IsAbstract: false }) - .Where(IsValidatorImplementationType); - - builder.Add(exports, lifetime); - - return builder; - } - - /// - /// Adds the specified type to the validator configuration for inspection. - /// - /// The to configure. - /// The type to be added to the validator configuration. - /// The instance for further configuration. - public static IValidatorBuilder Add(this IValidatorBuilder builder, Type type) - { - ArgumentNullException.ThrowIfNull(builder); - - builder.Services.AddOptions() - .Configure(options => options.Inspect([type])); - - return builder; - } - - /// - /// Adds the specified type to the validator configuration. - /// 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 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 IValidatorBuilder Add(this IValidatorBuilder builder, Type type, ServiceLifetime lifetime) - { - ArgumentNullException.ThrowIfNull(builder); - - builder.Services.AddOptions() - .Configure(options => options.Inspect([type])); - - builder.Services.Add(new ServiceDescriptor(type, type, lifetime)); - - return builder; - } - - /// - /// Adds the specified collection of types to the validator configuration for inspection. - /// - /// The to configure. - /// The collection of types to be added to the validator configuration. - /// The instance for further configuration. - public static IValidatorBuilder Add(this IValidatorBuilder builder, IEnumerable types) - { - ArgumentNullException.ThrowIfNull(builder); - - var typeList = types.ToList(); - - builder.Services.AddOptions() - .Configure(options => options.Inspect(typeList)); - - return builder; - } - - /// - /// Adds the specified collection of types to the validator configuration for inspection. - /// 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 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 IValidatorBuilder Add(this IValidatorBuilder builder, IEnumerable types, ServiceLifetime lifetime) - { - ArgumentNullException.ThrowIfNull(builder); - - var typeList = types.ToList(); - - builder.Services.AddOptions() - .Configure(options => options.Inspect(typeList)); - - builder.Services.Add(typeList.Select(export => new ServiceDescriptor(export, export, lifetime))); - - return builder; - } - - /// - /// 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 IValidatorBuilder Add(this IValidatorBuilder builder) - where TValidator : class, IValidator - { - return builder.Add(typeof(TValidator)); - } - - /// - /// Adds the specified validator type to the validator configuration with the specified lifetime. - /// - /// The type of the validator to add. - /// The to configure. - /// The lifetime scope of the validator in the service container. - /// The instance for further configuration. - public static IValidatorBuilder Add(this IValidatorBuilder builder, ServiceLifetime lifetime) - where TValidator : class, IValidator - { - return builder.Add(typeof(TValidator), lifetime); - } - - private static bool IsValidatorImplementationType(Type type) - { - return type.GetInterfaces().Any(IsValidatorType); - } -} diff --git a/src/request.validation/ValidatorExtensions.cs b/src/request.validation/ValidatorExtensions.cs deleted file mode 100644 index 7f0c69c..0000000 --- a/src/request.validation/ValidatorExtensions.cs +++ /dev/null @@ -1,23 +0,0 @@ -// Copyright (c) The Geekeey Authors -// SPDX-License-Identifier: EUPL-1.2 - -namespace Geekeey.Request.Validation; - -/// -/// Extension methods for . -/// -public static class ValidatorExtensions -{ - /// - /// Executes the validation logic for a specified instance of type and returns the validation result. - /// - /// The instance used to perform the validation. - /// The instance of type to validate. - /// The service provider available for nested validator resolution. - /// Per-call state shared across nested validation operations. - /// A object containing the results of the validation, including any problems encountered. - public static Validation Validate(this IValidator validator, T instance, IServiceProvider? serviceProvider = null, IReadOnlyDictionary? items = null) - { - return validator.Validate(new ValidationContext(instance, serviceProvider, items)); - } -}