feat: create request projects for basic CQRS
This commit is contained in:
commit
d614788e06
190 changed files with 12236 additions and 0 deletions
119
src/request.validation/Rule.cs
Normal file
119
src/request.validation/Rule.cs
Normal file
|
|
@ -0,0 +1,119 @@
|
|||
// Copyright (c) The Geekeey Authors
|
||||
// SPDX-License-Identifier: EUPL-1.2
|
||||
|
||||
using System.Linq.Expressions;
|
||||
|
||||
namespace Geekeey.Request.Validation;
|
||||
|
||||
internal abstract record Rule
|
||||
{
|
||||
protected Rule(Expression expression)
|
||||
{
|
||||
PropertyPath = PropertyPath.FromExpression(expression);
|
||||
}
|
||||
|
||||
protected PropertyPath GetPropertyPath()
|
||||
{
|
||||
return PropertyPathTransform(PropertyPath);
|
||||
}
|
||||
|
||||
public PropertyPath PropertyPath { get; }
|
||||
|
||||
public Severity Severity { get; init; } = Severity.Error;
|
||||
|
||||
public string? Code { get; init; }
|
||||
|
||||
public Func<PropertyPath, PropertyPath> PropertyPathTransform { get; init; } = static path => path;
|
||||
|
||||
public abstract IEnumerable<Problem> Validate(ValidationContext context);
|
||||
}
|
||||
|
||||
internal abstract record Rule<T, TProperty> : Rule
|
||||
{
|
||||
protected Rule(Expression expression)
|
||||
: base(expression)
|
||||
{
|
||||
}
|
||||
|
||||
public IReadOnlyList<IRuleStep<TProperty>> Steps { get; init; } = [];
|
||||
|
||||
public override IEnumerable<Problem> Validate(ValidationContext context)
|
||||
{
|
||||
if (context.Instance is T instance)
|
||||
{
|
||||
return Validate(instance, context);
|
||||
}
|
||||
|
||||
if (context.Instance is null && default(T) is null)
|
||||
{
|
||||
return Validate((T)context.Instance!, context);
|
||||
}
|
||||
|
||||
var actualType = context.Instance?.GetType().FullName ?? "null";
|
||||
throw new InvalidOperationException(
|
||||
$"Expected validation context instance of type '{typeof(T).FullName}', but got '{actualType}'.");
|
||||
}
|
||||
|
||||
protected abstract IEnumerable<Problem> Validate(T instance, ValidationContext context);
|
||||
}
|
||||
|
||||
internal sealed record PropertyRule<T, TProperty> : Rule<T, TProperty>
|
||||
{
|
||||
private readonly Func<T, TProperty> _accessor;
|
||||
|
||||
public PropertyRule(Expression<Func<T, TProperty>> expression) : base(expression)
|
||||
{
|
||||
_accessor = expression.Compile();
|
||||
}
|
||||
|
||||
protected override IEnumerable<Problem> Validate(T instance, ValidationContext context)
|
||||
{
|
||||
if (Steps.Count is 0)
|
||||
{
|
||||
return [];
|
||||
}
|
||||
|
||||
var value = _accessor(instance);
|
||||
var propertyPath = GetPropertyPath();
|
||||
return Steps.SelectMany(step => step.Validate(value, context, propertyPath, Code, Severity));
|
||||
}
|
||||
}
|
||||
|
||||
internal sealed record CollectionRule<T, TElement> : Rule<T, TElement>
|
||||
{
|
||||
private readonly Func<T, IEnumerable<TElement>> _accessor;
|
||||
|
||||
public CollectionRule(Expression<Func<T, IEnumerable<TElement>>> expression) : base(expression)
|
||||
{
|
||||
_accessor = expression.Compile();
|
||||
}
|
||||
|
||||
protected override IEnumerable<Problem> Validate(T instance, ValidationContext context)
|
||||
{
|
||||
if (Steps.Count is 0)
|
||||
{
|
||||
yield break;
|
||||
}
|
||||
|
||||
if (_accessor(instance) is not { } collection)
|
||||
{
|
||||
yield break;
|
||||
}
|
||||
|
||||
var index = 0;
|
||||
foreach (var element in collection)
|
||||
{
|
||||
var propertyPath = GetPropertyPath() + index;
|
||||
|
||||
foreach (var step in Steps)
|
||||
{
|
||||
foreach (var problem in step.Validate(element, context, propertyPath, Code, Severity))
|
||||
{
|
||||
yield return problem;
|
||||
}
|
||||
}
|
||||
|
||||
index++;
|
||||
}
|
||||
}
|
||||
}
|
||||
Loading…
Reference in a new issue