IEnumerable <ValidationResult> IValidatableDocument.Validate(SaveType saveType) { var parentPropertyName = EmbeddedDocumentUtility.GetParentPropertyName <T, TEmbeddedIn>((T)this); var validationContext = DocumentValidationContext <T> .Create((T)this, Parent, parentPropertyName, saveType); return(ValidationUtility.Validate(Validators, validationContext)); }
public void SucceedsForValidDataWithReference() { // Arrange. var validator = new DocumentUniquenessValidator <TestEntityWithReference, string>(x => x.Title) { Scope = new Expression <Func <TestEntityWithReference, object> >[] { x => x.Site } }; TestEntityWithReference.Create(new TestEntityWithReference { Site = Site.Create(new Site { Title = "Baz" }), Title = "Foo" }); var documentInstance = new TestEntityWithReference { Site = Site.Create(new Site { Title = "Bar" }), Title = "Foo" }; var context = DocumentValidationContext <TestEntityWithReference> .Create(documentInstance, SaveType.Any); // Act. var results = validator.Validate("Foo", context).ToList(); // Assert. Assert.That(results, Is.Empty); }
/// <summary> /// Validates the completed document context to ensure it is "correct" against the specification before generating /// the final document. /// </summary> /// <param name="context">The context containing the parsed sections of a query document..</param> /// <returns><c>true</c> if the rule passes, <c>false</c> otherwise.</returns> public override bool Execute(DocumentValidationContext context) { var fieldSelection = (FieldSelection)context.ActivePart; // inspect all declared arguments from the schema var allArgsValid = true; foreach (var argument in fieldSelection.Field.Arguments) { // when the argument is required but the schema defines no value // and it was not on the user query document this rule fails if (argument.TypeExpression.IsRequired && argument.DefaultValue == null && !fieldSelection.Arguments.ContainsKey(argument.Name.AsMemory())) { this.ValidationError( context, fieldSelection.Node, $"Missing Input Argument. The field '{fieldSelection.Name}' requires an input argument named '{argument.Name}'"); allArgsValid = false; } } return(allArgsValid); }
/// <summary> /// Validates the completed document context to ensure it is "correct" against the specification before generating /// the final document. /// </summary> /// <param name="context">The context containing the parsed sections of a query document..</param> /// <returns><c>true</c> if the rule passes, <c>false</c> otherwise.</returns> public override bool Execute(DocumentValidationContext context) { var ivdp = context.ActivePart as IInputValueDocumentPart; var graphType = ivdp.GraphType as IInputObjectGraphType; var requiredFields = graphType?.Fields.Where(x => x.TypeExpression.IsRequired).ToList(); var complexValue = ivdp.Value as QueryComplexInputValue; if (complexValue == null || requiredFields == null || !requiredFields.Any()) { this.ValidationError( context, ivdp.Node, $"Input type mismatch. The {ivdp.InputType} '{ivdp.Name}' was used like an {TypeKind.INPUT_OBJECT.ToString()} " + $"but contained no fields to evaluate. Check the schema definition for {ivdp.TypeExpression.TypeName}."); return(false); } var allFieldsAccountedFor = true; foreach (var field in requiredFields) { if (!complexValue.Arguments.ContainsKey(field.Name)) { this.ValidationError( context, ivdp.Node, $"The {ivdp.InputType} '{ivdp.Name}' requires a field named '{field.Name}'."); allFieldsAccountedFor = false; } } return(allFieldsAccountedFor); }
public void FailsForInvalidData() { // Arrange. var validator = new EmbeddedDocumentUniquenessValidator <TestBook, TestAuthor, string>(x => x.Title); var testBook = new TestBook { Title = "Bar" }; var documentInstance = new TestAuthor { Books = new List <TestBook> { new TestBook { Title = "Foo" }, testBook } }; var context = DocumentValidationContext <TestBook> .Create(testBook, documentInstance, "Books", SaveType.Any); // Act. var results = validator.Validate("Foo", context).ToList(); // Assert. Assert.That(results.Count, Is.EqualTo(1)); }
/// <summary> /// Registers a validation error with the local message collection as a critical error. The validation /// message will automatically be appended with the appropriate message extensions to reference the error being validated. /// </summary> /// <param name="context">The validation context in scope.</param> /// <param name="node">The node in scope when the error was generated.</param> /// <param name="message">The error message.</param> protected void ValidationError(DocumentValidationContext context, SyntaxNode node, string message) { var graphMessage = GraphExecutionMessage.FromValidationRule( this, message, node.Location.AsOrigin()); context.Messages.Add(graphMessage); }
public int TestValidation(string value) { // Arrange. var validator = new PresenceValidator <TestEntity>(); var instance = new TestEntity(); var context = DocumentValidationContext <TestEntity> .Create(instance, SaveType.Any); // Act. return(validator.Validate(value, context).Count()); }
/// <summary> /// Interpretes the syntax tree and generates a contextual document that can be transformed into /// a query plan. /// </summary> /// <param name="syntaxTree">The syntax tree to create a document for.</param> /// <returns>IGraphQueryDocument.</returns> public IGraphQueryDocument CreateDocument(ISyntaxTree syntaxTree) { Validation.ThrowIfNull(syntaxTree, nameof(syntaxTree)); // -------------------------------------------- // Step 1: Parse the syntax tree // -------------------------------------------- // Walk all nodes of the tree and on a "per node" basis perform actions // that are required of that node be it a specification validation rule or // an incremental addition to the document context being built. // // Note: All packages are rendered and then named fragment nodes are processed first // as they are required by any operations referenced elsewhere in the document // -------------------------------------------- var nodeProcessor = new DocumentConstructionRuleProcessor(); var docContext = new DocumentContext(_schema); var nodeContexts = new List <DocumentConstructionContext>(); foreach (var node in syntaxTree.Nodes) { var nodeContext = docContext.ForTopLevelNode(node); nodeContexts.Add(nodeContext); } nodeContexts.Sort(new TopLevelNodeProcessingOrder()); var completedAllSteps = nodeProcessor.Execute(nodeContexts); // -------------------------------------------- // Step 2: Validate the document parts // -------------------------------------------- // Inspect the document parts that were generated during part one and, as a whole, run additional // validation rules and perform final changes before constructing the final document. // e.g. ensure all fragments were called, all variables were referenced at least once etc. // -------------------------------------------- if (completedAllSteps) { var documentProcessor = new DocumentValidationRuleProcessor(); var validationContexts = new List <DocumentValidationContext>(); foreach (var part in docContext.Children) { var partContext = new DocumentValidationContext(docContext, part); validationContexts.Add(partContext); } documentProcessor.Execute(validationContexts); } // -------------------------------------------- // Step 3: Build out the final document // -------------------------------------------- return(docContext.ConstructDocument()); }
public int TestValidation(string value, int minimumLength, int maximumLength) { // Arrange. var validator = new StringLengthValidator <TestEntity>(minimumLength, maximumLength) { AllowNull = true }; var instance = new TestEntity(); var context = DocumentValidationContext <TestEntity> .Create(instance, SaveType.Any); // Act. return(validator.Validate(value, context).Count()); }
IEnumerable <ValidationResult> IValidatableDocument.Validate(SaveType saveType) { Errors.AddRange(ValidationUtility.Validate(PropertyValidators, DocumentValidationContext <T> .Create((T)this, saveType))); foreach (IValidatableDocument embeddedDocument in EmbeddedDocumentUtility.GetEmbeddedDocuments(this)) { Errors.AddRange(embeddedDocument.Validate(saveType)); } foreach (var objectValidator in ObjectValidators) { Errors.AddRange(objectValidator((T)this)); } return(Errors); }
/// <summary> /// Validates the completed document context to ensure it is "correct" against the specification before generating /// the final document. /// </summary> /// <param name="context">The context containing the parsed sections of a query document..</param> /// <returns><c>true</c> if the rule passes, <c>false</c> otherwise.</returns> public override bool Execute(DocumentValidationContext context) { var variable = (QueryVariable)context.ActivePart; if (!variable.IsReferenced) { this.ValidationError( context, variable.Node, $"The variable '{variable.Name}' was not used within the target operation. " + "All declared variables must be used at least once."); return(false); } return(true); }
/// <summary> /// Validates the completed document context to ensure it is "correct" against the specification before generating /// the final document. /// </summary> /// <param name="context">The context containing the parsed sections of a query document..</param> /// <returns><c>true</c> if the rule passes, <c>false</c> otherwise.</returns> public override bool Execute(DocumentValidationContext context) { var fragment = (QueryFragment)context.ActivePart; if (!fragment.IsReferenced) { this.ValidationError( context, fragment.Node, $"The named fragment '{fragment.Name}' was not referenced by an operation. " + "All declared fragments must be used at least once."); return(false); } return(true); }
/// <summary> /// Validates the specified node to ensure it is "correct" in the context of the rule doing the valdiation. /// </summary> /// <param name="context">The validation context encapsulating a <see cref="SyntaxNode"/> that needs to be validated.</param> /// <returns><c>true</c> if the node is valid, <c>false</c> otherwise.</returns> public override bool Execute(DocumentValidationContext context) { // anonymous operations will all present as ReadOnlyMemory<char>.Empty var operation = (QueryOperation)context.ActivePart; if (context.DocumentContext.Operations.Count > 1) { this.ValidationError( context, operation.Node, "A query document may declare an anonymous operation only if it exists by itself in a document. This document " + $"contains {context.DocumentContext.Operations.Count} total operation(s). Remove the other operations are " + "provide a name for every operation."); return(false); } return(true); }
/// <summary> /// Validates the completed document context to ensure it is "correct" against the specification before generating /// the final document. /// </summary> /// <param name="context">The context containing the parsed sections of a query document..</param> /// <returns><c>true</c> if the rule passes, <c>false</c> otherwise.</returns> public override bool Execute(DocumentValidationContext context) { var queryDirective = (QueryDirective)context.ActivePart; var directiveIsValid = true; // inspect all declared arguments from the schema foreach (var argument in queryDirective.Directive.Arguments) { if (argument.DefaultValue == null && !queryDirective.Arguments.ContainsKey(argument.Name.AsMemory())) { this.ValidationError( context, queryDirective.Node, $"Missing Input Argument. The directive '{queryDirective.Name}' requires an input argument named '{argument.Name}'"); directiveIsValid = false; } } return(directiveIsValid); }
/// <summary> /// Validates the completed document context to ensure it is "correct" against the specification before generating /// the final document. /// </summary> /// <param name="context">The context containing the parsed sections of a query document..</param> /// <returns> /// <c>true</c> if the rule passes, <c>false</c> otherwise.</returns> public override bool Execute(DocumentValidationContext context) { var argument = context.ActivePart as QueryInputArgument; var qvr = argument.Value as QueryVariableReferenceInputValue; // ensure the type expressions are compatible at the location used if (!qvr.Variable.TypeExpression.Equals(argument.TypeExpression)) { this.ValidationError( context, argument.Node, $"Invalid Variable Argument. The type expression for the variable used on the " + $"{argument.InputType} '{argument.Name}' could " + $"not be successfully coerced to the required type. Expected '{argument.TypeExpression}' but got '{qvr.Variable.TypeExpression}'. Double check " + $"the declared graph type of the variable and ensure it matches the required type of '{argument.Name}'."); return(false); } return(true); }
public void FailsForInvalidData() { // Arrange. var validator = new DocumentUniquenessValidator <TestEntity, string>(x => x.Title) { Scope = new Expression <Func <TestEntity, object> >[] { x => x.SiteID } }; TestEntity.Create(new TestEntity { SiteID = 1, Title = "Foo" }); var documentInstance = new TestEntity { SiteID = 1, Title = "Foo" }; var context = DocumentValidationContext <TestEntity> .Create(documentInstance, SaveType.Any); // Act. var results = validator.Validate("Foo", context).ToList(); // Assert. Assert.That(results, Has.Count.EqualTo(1)); }
/// <summary> /// Validates the completed document context to ensure it is "correct" against the specification before generating /// the final document. /// </summary> /// <param name="context">The context containing the parsed sections of a query document..</param> /// <returns><c>true</c> if the rule passes, <c>false</c> otherwise.</returns> public override bool Execute(DocumentValidationContext context) { var ivdp = context.ActivePart as IInputValueDocumentPart; var value = ivdp.Value; // variables do not have to supply a default value if (value == null && ivdp is QueryVariable) { return(true); } if (!this.EvaluateContextData(value)) { this.ValidationError( context, value.ValueNode, $"Invalid {ivdp.InputType}. The value for the input item named '{ivdp.Name}' could " + $"not be successfully coerced to the required type of '{ivdp.TypeExpression}'."); return(false); } return(true); }
/// <summary> /// Determines whether this instance can process the given context. The rule will have no effect on the node if it cannot /// process it. /// </summary> /// <param name="context">The context that may be acted upon.</param> /// <returns><c>true</c> if this instance can validate the specified node; otherwise, <c>false</c>.</returns> public override bool ShouldExecute(DocumentValidationContext context) { return(base.ShouldExecute(context) && context.ActivePart is QueryOperation operation && operation.OperationType == GraphCollection.Subscription); }
/// <summary> /// Determines whether this instance can process the given context. The rule will have no effect on the node if it cannot /// process it. /// </summary> /// <param name="context">The context that may be acted upon.</param> /// <returns><c>true</c> if this instance can validate the specified node; otherwise, <c>false</c>.</returns> public override bool ShouldExecute(DocumentValidationContext context) { return(base.ShouldExecute(context) && context.ActivePart is QueryOperation operation && operation.Name == string.Empty); }
/// <summary> /// Determines whether this instance can process the given context. The rule will have no effect on the input argument if it cannot /// process it. /// </summary> /// <param name="context">The context that may be acted upon.</param> /// <returns><c>true</c> if this instance can validate the specified input argument; otherwise, <c>false</c>.</returns> public override bool ShouldExecute(DocumentValidationContext context) { return(context.ActivePart is IInputValueDocumentPart ivdp && !(ivdp.Value is QueryVariableReferenceInputValue)); }
/// <summary> /// Determines whether this instance can process the given context. The rule will have no effect on the node if it cannot /// process it. /// </summary> /// <param name="context">The context that may be acted upon.</param> /// <returns><c>true</c> if this instance can validate the specified document part; otherwise, <c>false</c>.</returns> public override bool ShouldExecute(DocumentValidationContext context) { return context.Contains<TContextItem>(); }
/// <summary> /// Determines whether this instance can process the given context. The rule will have no effect on the input argument if it cannot /// process it. /// </summary> /// <param name="context">The context that may be acted upon.</param> /// <returns><c>true</c> if this instance can validate the specified input argument; otherwise, <c>false</c>.</returns> public override bool ShouldExecute(DocumentValidationContext context) { return(context.ActivePart is QueryInputArgument arg && arg.Value is QueryVariableReferenceInputValue); }
/// <summary> /// Validates the completed document context to ensure it is "correct" against the specification before generating /// the final document. /// </summary> /// <param name="context">The context containing the parsed sections of a query document..</param> /// <returns><c>true</c> if the rule passes, <c>false</c> otherwise.</returns> public override bool Execute(DocumentValidationContext context) { // due to the use of virtual fields used by controllers to make a dynamic schema, // this rule extend rule 5.2.3.1 to include the top-level operation and each virtual child field // has 1 and only 1 child field declaration up to and including a subscription action being located. // that is to say the nested fieldsets must not branch until AFTER a subscription field is encountered // as its this field that is registered as the subscription, not the virtual field paths /* * ----------------------------------------------------- * Valid: * ----------------------------------------------------- * subscription { * ctrlPath { * routePath1 { * routePath2 { * subscriptionAction { } * } * } * } * } * * * subscription { * subscriptionAction { } * } * * * ----------------------------------------------------- * Invalid: * ----------------------------------------------------- * subscription { * ctrlPath { * routePath1 { * routePath2 { * subscriptionAction1 { } * subscriptionAction2 { } // two subscription actions encountered (must be 1) * } * } * } * } * * subscription { * ctrlPath { * routePath1 { * routePath2 { * queryActionField { } // not a subscription field * } * } * } * } * * subscription { * controller { * routePath1 { * routePath2 { * subscriptionActionField2 { } * } * } * subscriptionActionField2 { } // split pathing allows for two possible subscription fields * //(must encounter only 1) * } * } */ var operation = context.ActivePart as QueryOperation; var fieldCollection = operation?.FieldSelectionSet; while (fieldCollection != null && fieldCollection.Count == 1) { // did we encounter a field collection with exactly one child that is not virtual? // i.e. one "top-level user action" to be called for the subscription? var childField = fieldCollection[0]; if (!childField.GraphType.IsVirtual) { return(true); } fieldCollection = childField.FieldSelectionSet; } this.ValidationError( context, operation.Node, "Invalid Subscription. Expected exactly 1 child field, " + $"recieved {fieldCollection?.Count ?? 0} child fields at {fieldCollection?.RootPath.DotString() ?? "-null-"}."); return(false); }
/// <summary> /// Determines whether this instance can process the given context. The rule will have no effect on the input argument if it cannot /// process it. /// </summary> /// <param name="context">The context that may be acted upon.</param> /// <returns><c>true</c> if this instance can validate the specified input argument; otherwise, <c>false</c>.</returns> public override bool ShouldExecute(DocumentValidationContext context) { return(context.ActivePart is IInputValueDocumentPart ivdp && ivdp.Value is QueryComplexInputValue); }