Repository navigation
Add the validation pipeline, custom validators and condition evaluation - #2727
Merged
Merged
Conversation
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Closes #2685
Adds the validation pipeline of spec 4.2 and 4.4 (B-14). The SDK gets the contract (
IFieldValidator<TField>,IEntryValidator<T>,ValidationContext,ValidationResult,ValidationError,ValidationException,ValidationMode,ValidationErrorCodes). Core getsValidationPipeline, which validates aSchemaTypeagainst an entry's values (a dictionary keyed by API field name) and an optional typed entry, in the order declarative rules, custom field validators, entry validators. It also getsConditionEvaluatorandConditionJson, so aConditionevaluates the same from C# and from JSON.ConditionJsonreuses theschema.jsoncondition reader and writer; the only change to those files is thatWriteConditionandReadConditionare nowinternal.Uniqueness, field type coercion,
IContentServiceand the DI registration are not here (#2693, #2684, #2695). A comment in the pipeline marks where uniqueness and reference integrity slot in.Dependencies
No new package in
src. The test projectFluentCMS.Core.TestsreferencesMicrosoft.Extensions.DependencyInjection10.0.14 (same train as the EF packages) so validators can be registered in aServiceCollection. The pipeline itself resolves validators withIServiceProvider.GetService(typeof(IEnumerable<...>)), so Core needs no DI abstractions package.Acceptance
ValidationError { Path, Code, Message }, codes below; the HTTP mapping is a later issue)Draftmode passes,Publishmode returns the errors; the service call is IContentService and IContentService<T> with optimistic concurrency #2693)Error codes
RequiredMinLengthMinLength, or fewer items on a multiple fieldMaxLengthMaxLength, or more items on a multiple fieldBelowMinimumMinimumAboveMaximumMaximumPatternMismatchPatternNotAnAllowedOptionInvalidValueCustomRequiredByConditionRequiredWhenholds and the field has no valueBeyond the spec
ValidationContextalso hasPath(the full path of the current field, soAddError(code, message)onseo.metaTitlelands at the right path), a read-onlyErrorslist, and a public constructor so plugin authors can unit-test validators. The pipeline creates one context per field that shares the run's error list; for a field inside a component or block,Valuesare its siblings in that component or block.ValidationContext.Containeris the type that declares the field: the entry type at the top level, the component or block type inside one (Typestays the entry type).AddErrorwith an empty code usesFCMS2109.AddError(code, message)throwsInvalidOperationExceptionin an entry validator (no current field).VisibleWhenfalse) are skipped completely: no declarative rule, no custom field validator, no descent into components or blocks.RequiredandRequiredWhenwould both fail, onlyFCMS2101is reported. A format rule (length, range, pattern, option) is skipped for an empty value.Multiplefield and for a blocks field (message says "items"). Range, pattern and options on aMultiplefield check each item, reported atfield[i](tags[1]).Minimum/Maximumapply to numbers only. Date and date-time bounds wait for a date-aware range in the model, since nothing says whether a bound means days or seconds. A float beyond what a decimal holds counts as the nearest decimal bound, so it is out of range in its own direction.Settings[options]ordinally; no check if the field has no options.RegexOptions.CultureInvariant, a 1 second timeout, one compiledRegexper field per pipeline. A timeout counts as a mismatch. The pattern matches anywhere in the value unless anchored; theRegularExpressiondoc says so.IReadOnlyDictionary<string, object?>(orIDictionary) keyed by API name, or a CLR object whose public properties are read by reflection, names converted to camelCase. A dictionary block carries its block name undertype; a CLR block is matched to a block by its class (SchemaType.ClrType). A block with no or an unknowntype, or one the field does not list, isFCMS2107onfield[i]. A null or empty component is not descended into, so required fields inside it are not reported; require the component itself instead.IsEmptytreats a whitespace-only string as empty (theConditionField.IsEmptydoc now says so), and a defaultRef<T>, a defaultMediaRefandGuid.Emptyas empty; any otherIEnumerableis empty when it has no items.Equal/Inagainst a list value (multi select) match when any item matches; an empty list equalsnull. ARef<T>compares as its id string.object. Anullvalue is only passed to validators of reference types and ofobject;IFieldValidator<int>is skipped for anullint. They run for fields in components and blocks too.ConditionJsonoffersWrite(Condition, Utf8JsonWriter),Read(JsonElement),ToJsonandParse; invalid input throwsJsonExceptionwith the schema reader's messages.Readruns the schema reader on an emptySchemaBuilderto get them, so there is no second parser.ConditionEvaluator,ConditionJsonandValidationPipelineare public in Core;DeclarativeValidatoris internal.