2026-05-28 20:12:00 +02:00
|
|
|
// 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<Type, ValidatorInvoker> ValidatorsHandlers = new();
|
|
|
|
|
|
|
|
|
|
private readonly IServiceProvider _serviceProvider;
|
|
|
|
|
|
|
|
|
|
public DispatchingValidator(IServiceProvider serviceProvider)
|
|
|
|
|
{
|
|
|
|
|
_serviceProvider = serviceProvider;
|
|
|
|
|
}
|
|
|
|
|
|
2026-05-28 20:39:51 +02:00
|
|
|
public static void ClearCache(Type[]? _)
|
|
|
|
|
{
|
|
|
|
|
ValidatorsHandlers.Clear();
|
|
|
|
|
}
|
|
|
|
|
|
2026-05-28 20:12:00 +02:00
|
|
|
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<T> : ValidatorInvoker
|
|
|
|
|
{
|
|
|
|
|
public override Validation Validate(ValidationContext context, IServiceProvider serviceProvider)
|
|
|
|
|
{
|
2026-05-29 23:05:24 +02:00
|
|
|
var options = serviceProvider.GetRequiredService<IOptions<RequestValidatorOptions>>().Value;
|
2026-05-28 20:12:00 +02:00
|
|
|
|
|
|
|
|
var validators = options.GetValidators<T>(serviceProvider);
|
|
|
|
|
|
|
|
|
|
var problems = new List<Problem>();
|
|
|
|
|
|
|
|
|
|
foreach (var validator in validators)
|
|
|
|
|
{
|
|
|
|
|
if (validator.Validate(context) is { IsValid: false, Problems: { } result })
|
|
|
|
|
{
|
|
|
|
|
problems.AddRange(result);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return new Validation(problems);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|