内容目录
WorkflowDefinitionValidator 是整个验证入口,上一个非常复杂的对象结构。
参考:https://github.com/serverlessworkflow/specification/blob/main/specification.md
通过依赖注入,继续检查下一层的字段。
/// <summary>
/// Represents the service used to validate <see cref="WorkflowDefinition"/>s
/// </summary>
public class WorkflowDefinitionValidator
: AbstractValidator<WorkflowDefinition>
{
/// <summary>
/// Initializes a new <see cref="WorkflowDefinitionValidator"/>
/// </summary>
/// <param name="serviceProvider">The current <see cref="IServiceProvider"/></param>
public WorkflowDefinitionValidator(IServiceProvider serviceProvider)
{
this.ServiceProvider = serviceProvider;
this.RuleFor(w => w.Id)
.NotEmpty()
.When(w => string.IsNullOrWhiteSpace(w.Key));
this.RuleFor(w => w.Key)
.NotEmpty()
.When(w => string.IsNullOrWhiteSpace(w.Id));
this.RuleFor(w => w.Name)
.NotEmpty();
this.RuleFor(w => w.Version)
.NotEmpty();
this.RuleFor(w => w.ExpressionLanguage)
.NotEmpty();
this.RuleFor(w => w.Start!)
.Must(ReferenceExistingState)
.When(w => w.Start != null)
.WithMessage((workflow, start) => $"Failed to find the state with name '{start.StateName}' specified by the workflow's start definition");
this.RuleFor(w => w.StartStateName!)
.Must(ReferenceExistingState)
.When(w => w.StartStateName != null)
.WithMessage((workflow, startState) => $"Failed to find the state with name '{startState}' specified by the workflow's start definition");
this.RuleFor(w => w.Events)
.Must(events => events!.Select(s => s.Name).Distinct().Count() == events!.Count)
.When(w => w.Events != null)
.WithMessage("Duplicate EventDefinition name(s) found");
this.RuleFor(w => w.Events)
.SetValidator(new CollectionPropertyValidator<EventDefinition>(this.ServiceProvider))
.When(w => w.Events != null);
this.RuleFor(w => w.Functions)
.Must(functions => functions!.Select(s => s.Name).Distinct().Count() == functions!.Count)
.When(w => w.Functions != null)
.WithMessage("Duplicate FunctionDefinition name(s) found");
this.RuleFor(w => w.Functions!)
.SetValidator(new FunctionDefinitionCollectionValidator())
.When(w => w.Functions != null);
this.RuleFor(w => w.Retries)
.Must(retries => retries!.Select(s => s.Name).Distinct().Count() == retries!.Count)
.When(w => w.Retries != null)
.WithMessage("Duplicate RetryPolicyDefinition name(s) found");
this.RuleFor(w => w.Retries)
.SetValidator(new CollectionPropertyValidator<RetryDefinition>(this.ServiceProvider))
.When(w => w.Retries != null);
this.RuleFor(w => w.Auth!)
.Must(auths => auths.Select(s => s.Name).Distinct().Count() == auths.Count)
.When(w => w.Auth != null)
.WithMessage("Duplicate AuthenticationDefinition name(s) found");
this.RuleFor(w => w.Auth!)
.SetValidator(new CollectionPropertyValidator<AuthenticationDefinition>(this.ServiceProvider))
.When(w => w.Auth != null);
this.RuleFor(w => w.States)
.NotEmpty();
this.RuleFor(w => w.States)
.Must(states => states.Select(s => s.Name).Distinct().Count() == states.Count)
.When(w => w.States != null)
.WithMessage("Duplicate StateDefinition name(s) found");
this.RuleFor(w => w.States)
.SetValidator(new WorkflowStatesPropertyValidator(this.ServiceProvider))
.When(w => w.States != null);
}
/// <summary>
/// Gets the current <see cref="IServiceProvider"/>
/// </summary>
protected IServiceProvider ServiceProvider { get; }
/// <inheritdoc/>
public override ValidationResult Validate(ValidationContext<WorkflowDefinition> context)
{
ValidationResult validationResult = base.Validate(context);
if (context.InstanceToValidate.States != null
&& !context.InstanceToValidate.States.Any(s => s.End != null))
validationResult.Errors.Add(new ValidationFailure("End", $"The workflow's main control flow must specify an EndDefinition"));
return validationResult;
}
/// <summary>
/// Determines whether or not the specified <see cref="StartDefinition"/> references an existing <see cref="StateDefinition"/>
/// </summary>
/// <param name="workflow">The <see cref="WorkflowDefinition"/> to validate</param>
/// <param name="start">The <see cref="StartDefinition"/> to check</param>
/// <returns>A boolean indicating whether or not the specified <see cref="StateDefinition"/> exists</returns>
protected virtual bool ReferenceExistingState(WorkflowDefinition workflow, StartDefinition start)
{
return workflow.TryGetState(start.StateName, out _);
}
/// <summary>
/// Determines whether or not the specified <see cref="StartDefinition"/> references an existing <see cref="StateDefinition"/>
/// </summary>
/// <param name="workflow">The <see cref="WorkflowDefinition"/> to validate</param>
/// <param name="startStateName">The name of the start <see cref="StateDefinition"/></param>
/// <returns>A boolean indicating whether or not the specified <see cref="StateDefinition"/> exists</returns>
protected virtual bool ReferenceExistingState(WorkflowDefinition workflow, string startStateName)
{
return workflow.TryGetState(startStateName, out _);
}
}
集合 PropertyValidator<WorkflowDefinition, IEnumerable<TElement>?>
验证结构:
/// <summary>
/// Represents the service used to validate a workflow's <see cref="ICollection{T}"/>s
/// </summary>
internal class CollectionPropertyValidator<TElement>
: PropertyValidator<WorkflowDefinition, IEnumerable<TElement>?>
{
/// <summary>
/// Initializes a new <see cref="CollectionPropertyValidator{TElement}"/>
/// </summary>
/// <param name="serviceProvider">The current <see cref="IServiceProvider"/></param>
public CollectionPropertyValidator(IServiceProvider serviceProvider)
{
this.ServiceProvider = serviceProvider;
}
/// <inheritdoc/>
public override string Name => "CollectionValidator";
/// <summary>
/// Gets the current <see cref="IServiceProvider"/>
/// </summary>
protected IServiceProvider ServiceProvider { get; }
/// <inheritdoc/>
public override bool IsValid(ValidationContext<WorkflowDefinition> context, IEnumerable<TElement>? value)
{
int index = 0;
if (value == null)
return true;
foreach (TElement elem in value)
{
IEnumerable<IValidator<TElement>> validators = this.ServiceProvider.GetServices<IValidator<TElement>>();
foreach (IValidator<TElement> validator in validators)
{
ValidationResult validationResult = validator.Validate(elem);
if (validationResult.IsValid)
continue;
foreach (var failure in validationResult.Errors)
{
context.AddFailure(failure);
}
return false;
}
index++;
}
return true;
}
}
/// <summary>
/// Represents the <see cref="PropertyValidator"/> used to validate a <see cref="FunctionDefinition"/> collection
/// </summary>
internal class FunctionDefinitionCollectionValidator
: PropertyValidator<WorkflowDefinition, IEnumerable<FunctionDefinition>>
{
/// <inheritdoc/>
public override string Name => "FunctionDefinitionCollection";
/// <inheritdoc/>
public override bool IsValid(ValidationContext<WorkflowDefinition> context, IEnumerable<FunctionDefinition> value)
{
WorkflowDefinition workflow = context.InstanceToValidate;
int index = 0;
IValidator<FunctionDefinition> validator = new FunctionDefinitionValidator(workflow);
foreach (FunctionDefinition function in value)
{
ValidationResult validationResult = validator.Validate(function);
if (validationResult.IsValid)
{
index++;
continue;
}
foreach(var failure in validationResult.Errors)
{
context.AddFailure(failure);
}
return false;
}
return true;
}
}
文章评论