/// <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(DocumentConstructionContext context) { var variable = context.FindContextItem <QueryVariable>(); var kind = variable.GraphType?.Kind ?? TypeKind.NONE; switch (kind) { case TypeKind.SCALAR: case TypeKind.ENUM: case TypeKind.INPUT_OBJECT: return(true); default: var errorName = variable.GraphType?.Name ?? "{null}"; var errorKind = variable.GraphType == null ? string.Empty : ", which is of kind '{variable.GraphType.Kind.ToString()}'"; this.ValidationError( context, $"Invalid Variable Graph Type. The variable named '${variable.Name}' references the graph type " + $"'{errorName}'{errorKind}. Only " + $"{TypeKind.SCALAR.ToString()}, {TypeKind.ENUM.ToString()} and '{TypeKind.INPUT_OBJECT.ToString()}' are allowed for " + "variable declarations."); return(false); } }
/// <summary> /// Executes the construction step 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(DocumentConstructionContext context) { // at this stage we know thta we've encountered a complexvalue node // we can also garuntee that the active input value on the request is going to be the value container // for that ComplexvalueNode. It is to that container that we are adding a new argument. // // take for instance this sequence: // // field(arg1: {childArg1: "value" childArg2: value} ) // // we are pointing at the argument "arg1" // which is a complexargument type having a value of QueryComplexInputValue which is a container of arguments // it is to that container that we are adding this new argument "childArg1" indicated by this InputItemNode // on the context. // // Note: this operation could be nested N levels deep such as with // field(arg1: { childArg1: {subChildArg1: value, subChildArg1: value} childArg2: 5} ) // the scenario would be valid for: // adding childArg1 or childArg2 to arg1 // adding subChildArg1 or subChildArg2 to childArg1 var node = (InputItemNode)context.ActiveNode; var inputObject = context.FindContextItem <QueryInputValue>() as QueryComplexInputValue; var ownerGraphType = inputObject.OwnerArgument.GraphType as IInputObjectGraphType; var field = ownerGraphType.Fields[node.InputName.ToString()]; var graphType = context.DocumentContext.Schema.KnownTypes.FindGraphType(field); var argument = new QueryInputArgument(node, graphType, field.TypeExpression); context.AddDocumentPart(argument); 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(DocumentConstructionContext context) { return(base.ShouldExecute(context) && ((FieldNode)context.ActiveNode) .FieldName .Span .SequenceNotEqual(Constants.ReservedNames.TYPENAME_FIELD.AsSpan())); }
/// <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(DocumentConstructionContext context) { if (context.ActiveNode is NamedFragmentNode) { return(true); } if (context.ActiveNode is OperationTypeNode otn) { var operationType = Constants.ReservedNames.FindOperationTypeByKeyword(otn.OperationType.ToString()); if (operationType == GraphCollection.Unknown) { this.ValidationError( context, $"Invalid Executable Definition. Expected executable definition of type '{nameof(OperationTypeNode)}' " + $"or '{nameof(NamedFragmentNode)}' but recieved '{otn.OperationName.ToString()}'."); return(false); } return(true); } this.ValidationError( context, $"Invalid Executable Definition. Expected executable definition of type '{nameof(OperationTypeNode)}' " + $"or '{nameof(NamedFragmentNode)}' but recieved '{context.ActiveNode?.GetType().FriendlyName() ?? "-nothing-"}'."); return(false); }
/// <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(DocumentConstructionContext context) { var fragmnet = context.FindContextItem <QueryFragment>(); if (string.IsNullOrEmpty(fragmnet.TargetGraphTypeName)) { return(true); } // shouldn't be false at this step, but just in case fail out if (fragmnet.GraphType == null) { return(false); } if (!ALLOWED_TYPE_KINDS.Contains(fragmnet.GraphType.Kind)) { this.ValidationError( context, $"The fragment declares a target graph type of '{fragmnet.GraphType.Name}' " + $"of kind '{fragmnet.GraphType.Kind.ToString()}' but " + $"fragments can only target graph types of kind {ALLOWED_TYPE_KIND_STRING}."); 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(DocumentConstructionContext context) { var fragment = context.FindContextItem <QueryFragment>(); // allow inline fragments to not have a target graph type (they inherit their parent's type) if (fragment.TargetGraphTypeName == string.Empty) { this.ValidationError( context, "Invalid Fragment. Fragments must declare a target type using the 'on' keyword."); return(false); } if (context.DocumentContext.Schema.KnownTypes.Contains(fragment.TargetGraphTypeName)) { return(true); } this.ValidationError( context, $"The fragment declares a target type of '{fragment.TargetGraphTypeName}' but no graph type exists " + $"on the target schema by that name."); return(false); }
/// <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(DocumentConstructionContext context) { var node = (DirectiveNode)context.ActiveNode; var queryDirective = context.FindContextItem <QueryDirective>(); if (queryDirective == null) { return(false); } var location = node.ParentNode?.DirectiveLocation(); if (!location.HasValue || !queryDirective.Directive.Locations.HasFlag(location)) { var locationName = location.HasValue ? location.Value.ToString() : "unknown"; var allowedLocations = queryDirective.Directive.Locations.GetIndividualFlags <DirectiveLocation>(); var allowedString = string.Join(", ", allowedLocations.Select(x => x.ToString())); this.ValidationError( context, $"Invalid directive location. Attempted use of '{queryDirective.Directive.Name}' at location '{locationName}'. " + $"Allowed Locations: {allowedString}"); } 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(DocumentConstructionContext context) { if (!base.ShouldExecute(context)) { return(false); } if (context.GraphType == null) { return(false); } if (!this.AllowedContextGraphTypeKinds.Contains(context.GraphType.Kind)) { return(false); } var targetGraphType = this.ExtractTargetGraphType(context); if (targetGraphType == null) { return(false); } if (!this.AllowedTargetGraphTypeKinds.Contains(targetGraphType.Kind)) { 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(DocumentConstructionContext context) { // both objects should exist if the rule chain is followed // but do a null check just in case var fragmentPointer = (FragmentSpreadNode)context.ActiveNode; var targetGraphType = this.ExtractTargetGraphType(context); var contextGraphType = context.GraphType; if (targetGraphType == null || contextGraphType == null) { return(false); } var rulePassed = this.CanAcceptGraphType(context.DocumentContext.Schema, contextGraphType, targetGraphType); if (!rulePassed) { this.ValidationError( context, $"The named fragment '{fragmentPointer.PointsToFragmentName.ToString()}' has a target graph type " + $"named '{targetGraphType.Name}' (Kind: '{targetGraphType.Kind.ToString()}') which cannot be coerced " + $"into the current selection set's target graph type of '{contextGraphType.Name}'."); } return(rulePassed); }
/// <summary> /// Executes the construction step 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(DocumentConstructionContext context) { var fragmentNode = (FragmentNode)context.ActiveNode; var queryFragment = new QueryFragment(fragmentNode); context.AddDocumentPart(queryFragment); return(true); }
/// <summary> /// Executes the construction step 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(DocumentConstructionContext context) { var variable = context.FindContextItem <QueryVariable>(); var graphType = context.DocumentContext.Schema.KnownTypes.FindGraphType(variable.TypeExpression.TypeName); variable.AttachGraphType(graphType); return(true); }
/// <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="message">The error message.</param> protected void ValidationError(DocumentConstructionContext context, string message) { var graphMessage = GraphExecutionMessage.FromValidationRule( this, message, context.ActiveNode.Location.AsOrigin()); context.Messages.Add(graphMessage); }
/// <summary> /// Executes the construction step 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(DocumentConstructionContext context) { var node = (VariableNode)context.ActiveNode; var queryVariable = new QueryVariable(node); context.AddDocumentPart(queryVariable); 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(DocumentConstructionContext context) { // TEMP Measure // Delete this rule completely once subscriptions are added this.ValidationError( context, "Subscriptions are not yet supported on this version of graphql-aspnet."); return(false); }
/// <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(DocumentConstructionContext context) { var node = (VariableValueNode)context.ActiveNode; var queryOperation = context.FindContextItem <QueryOperation>(); var queryValue = context.FindContextItem <QueryInputValue>() as QueryVariableReferenceInputValue; var variable = queryOperation.Variables[node.Value.ToString()]; variable.MarkAsReferenced(); queryValue?.AssignVariableReference(variable); return(true); }
/// <summary> /// Executes the construction step 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(DocumentConstructionContext context) { var node = (InputItemNode)context.ActiveNode; var queryDirective = context.FindContextItem <QueryDirective>(); var fieldArg = queryDirective.Directive.Arguments[node.InputName.ToString()]; var graphType = context.DocumentContext.Schema.KnownTypes.FindGraphType(fieldArg.TypeExpression.TypeName); var argument = new QueryInputArgument(node, graphType, fieldArg.TypeExpression); context.AddDocumentPart(argument); return(true); }
/// <summary> /// Executes the construction step 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(DocumentConstructionContext context) { // for whatever the active argument in context is // it could be an argument on a field, on a directive or // even an argument that is part of a nested input object inside a field argument set // make a value container for the active value // then add it to the context. var node = (InputValueNode)context.ActiveNode; var queryValue = QueryInputValueFactory.CreateInputValue(node); context.AddDocumentPart(queryValue); 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(DocumentConstructionContext context) { // only generate a new selection set if we are in context of // an operation or a field with child fields if (!base.ShouldExecute(context)) { return(false); } // only executing a new collection as a child of an existing field or operation renders a new selection set in the documents // other areas just append to the current scope of fields already in context. return(context.ActiveNode.ParentNode is FieldNode); }
/// <summary> /// Attempts to find the target graph type of the active spread node on the context. /// </summary> /// <param name="context">The context to extract from.</param> /// <returns>The found graph type or null if the fragment is not found or /// has no defined graph type.</returns> private IGraphType ExtractTargetGraphType(DocumentConstructionContext context) { var node = context.ActiveNode as FragmentSpreadNode; if (node == null) { return(null); } var namedFragment = context.DocumentContext.Fragments.FindFragment(node.PointsToFragmentName.ToString()); return(namedFragment?.GraphType); }
/// <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(DocumentConstructionContext context) { var node = (FieldNode)context.ActiveNode; var newField = context.FindContextItem <FieldSelection>(); // do a fast lookup before enumerating the field selection to determine // if there would even be a name collision. if (!context.SelectionSet.ContainsAlias(node.FieldAlias)) { return(true); } var isValid = true; var existingFields = context.SelectionSet.FindFieldsOfAlias(node.FieldAlias); foreach (var existingField in existingFields) { // we may iterate through the field we are adding, it can exist with itself // just skip it if (existingField == newField) { continue; } // fields with the same name in a given context // but targeting non-intersecting types can safely co-exist // in the same selection set. if (this.CanCoExist(context.DocumentContext.Schema, context.SelectionSet, existingField, newField)) { continue; } // fields that could cause a name collision for a type // must be mergable (i.e. have the same shape/signature). if (this.AreSameShape(existingField, newField)) { continue; } this.ValidationError( context, $"The selection set already contains a field with a name or alias of '{newField.Node.FieldAlias.ToString()}'. " + "An attempt was made to add another field with the same name or alias to the selection set but with a different " + "return graph type or input arguments. Fields with the same output name must have identicial signatures " + "within a single selection set."); isValid = false; } return(isValid); }
/// <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(DocumentConstructionContext context) { var node = (VariableCollectionNode)context.ActiveNode; if (!(node.ParentNode is OperationTypeNode)) { var targetNode = node.ParentNode?.GetType().FriendlyName() ?? "-none-"; this.ValidationError( context, $"Variable collections may only exist on operations. Current Target: '{targetNode}'"); } return(true); }
/// <summary> /// Executes the construction step 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(DocumentConstructionContext context) { var node = (FragmentSpreadNode)context.ActiveNode; QueryFragment fragment = context.DocumentContext.Fragments.FindFragment(node.PointsToFragmentName.ToString()); context.AddDocumentPart(fragment); fragment.MarkAsReferenced(); context.AppendNodes(fragment.Node.Children); context.BeginNewDocumentScope(); context.DocumentScope.RestrictFieldsToGraphType(fragment.GraphType); 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(DocumentConstructionContext context) { var node = (DirectiveNode)context.ActiveNode; var directive = context.DocumentContext.Schema.KnownTypes.FindDirective(node.DirectiveName.ToString()); if (directive == null || directive.Kind != TypeKind.DIRECTIVE) { this.ValidationError( context, $"The target schema does not contain a directive named '{node.DirectiveName.ToString()}'."); } 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(DocumentConstructionContext context) { var node = (InputItemCollectionNode)context.ActiveNode; if (!(node.ParentNode is FieldNode) && !(node.ParentNode is DirectiveNode) && !(node.ParentNode is ComplexValueNode)) { this.ValidationError( context, $"The node, of type '{node.ParentNode?.GetType().Name ?? "-none-"}' cannot contain a collection of input arguments. " + $"Input arguments are only valid on a '{typeof(FieldNode).Name}' and '{typeof(DirectiveNode).Name}' "); } return(true); }
/// <summary> /// Determines where this context is in a state such that it should continue processing its children. Returning /// false will cease processing child nodes under the active node of this context. This can be useful /// if/when a situation in a parent disqualifies all other nodes in the tree. This step is always executed /// even if the primary execution is skipped. /// </summary> /// <param name="context">The context.</param> /// <returns><c>true</c> if child node rulesets should be executed, <c>false</c> otherwise.</returns> public override bool ShouldAllowChildContextsToExecute(DocumentConstructionContext context) { // if we cant determine the operation type we cant deteremine the root graph type // and cant effectively parse the query document. var node = (OperationTypeNode)context.ActiveNode; var operationType = Constants.ReservedNames.FindOperationTypeByKeyword(node.OperationType.ToString()); if (!context.DocumentContext.Schema.OperationTypes.ContainsKey(operationType)) { 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(DocumentConstructionContext context) { var node = (InputItemNode)context.ActiveNode; var fieldSelection = context.FindContextItem <FieldSelection>(); if (!fieldSelection.Field.Arguments.ContainsKey(node.InputName.ToString())) { this.ValidationError( context, $"The field '{fieldSelection.Name}' does not define an input argument named '{node.InputName.ToString()}'. Input arguments " + $"must be defined on the field in the target schema."); 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(DocumentConstructionContext context) { var namedFragment = (NamedFragmentNode)context.ActiveNode; if (context.DocumentContext.Fragments.ContainsKey(namedFragment.FragmentName.ToString())) { this.ValidationError( context, $"Duplicate Fragment Name. The name '{namedFragment.FragmentName.ToString()}' must be unique in this document. Ensure that each " + "fragment in the document is unique (case-sensitive)."); 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(DocumentConstructionContext context) { var queryVariable = context.FindContextItem <QueryVariable>(); if (!queryVariable.TypeExpression.IsValid) { this.ValidationError( context, "Unknown Graph Type. Could not determine the graph type expression of the variable " + $"named '{queryVariable.Name}'. Double check that your variable declaration is correct."); 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(DocumentConstructionContext context) { var field = context.FindContextItem <FieldSelection>(); if (field.GraphType.Kind.IsLeafKind() && context.ActiveNode.Children.Any <FieldCollectionNode>()) { this.ValidationError( context, $"The graph type '{field.GraphType.Name}' is of kind '{field.GraphType.Kind.ToString()}'. It cannot declare a fieldset " + "to return."); 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(DocumentConstructionContext context) { var node = (InputItemNode)context.ActiveNode; var directive = context.FindContextItem <QueryDirective>(); if (directive.Arguments.ContainsKey(node.InputName)) { this.ValidationError( context, $"The directive '{directive.Name}' already contains an input argument named '{node.InputName.ToString()}'. Input arguments " + "must be unique per directive instance."); return(false); } return(true); }