protected override ISyntaxVisitorAction Enter( DocumentNode node, IDocumentValidatorContext context) { context.Names.Clear(); for (int i = 0; i < node.Definitions.Count; i++) { IDefinitionNode definition = node.Definitions[i]; if (definition.Kind == NodeKind.FragmentDefinition) { string fragmentName = ((FragmentDefinitionNode)definition).Name.Value; if (!context.Names.Add(fragmentName)) { context.Errors.Add( ErrorBuilder.New() .SetMessage( "There are multiple fragments with the name " + $"`{fragmentName}`.") .AddLocation(definition) .SetExtension("fragment", fragmentName) .SpecifiedBy("sec-Fragment-Name-Uniqueness") .Build()); } } } context.Names.Clear(); return(Continue); }
protected override ISyntaxVisitorAction Enter( VariableDefinitionNode node, IDocumentValidatorContext context) { base.Enter(node, context); var variableName = node.Variable.Name.Value; context.Unused.Add(variableName); context.Declared.Add(variableName); if (context.Schema.TryGetType( node.Type.NamedType().Name.Value, out INamedType type) && !type.IsInputType()) { context.Errors.Add(context.VariableNotInputType(node, variableName)); } if (!context.Names.Add(variableName)) { context.Errors.Add(context.VariableNameNotUnique(node, variableName)); } return(Skip); }
protected override ISyntaxVisitorAction Enter( ArgumentNode node, IDocumentValidatorContext context) { if (context.Directives.TryPeek(out DirectiveType directive)) { if (directive.Arguments.TryGetField(node.Name.Value, out Argument? argument)) { context.InputFields.Push(argument); context.Types.Push(argument.Type); return(Continue); } context.UnexpectedErrorsDetected = true; return(Skip); } if (context.OutputFields.TryPeek(out IOutputField field)) { if (field.Arguments.TryGetField(node.Name.Value, out IInputField? argument)) { context.InputFields.Push(argument); context.Types.Push(argument.Type); return(Continue); } context.UnexpectedErrorsDetected = true; return(Skip); } context.UnexpectedErrorsDetected = true; return(Skip); }
protected override ISyntaxVisitorAction Enter( VariableDefinitionNode node, IDocumentValidatorContext context) { context.Variables[node.Variable.Name.Value] = node; return(Continue); }
public void TwoOperationsThatShareVariableName() { // arrange IDocumentValidatorContext context = ValidationUtils.CreateContext(); DocumentNode query = Utf8GraphQLParser.Parse(@" query A($atOtherHomes: Boolean) { ...HouseTrainedFragment } query B($atOtherHomes: Boolean) { ...HouseTrainedFragment } fragment HouseTrainedFragment on Query { dog { isHousetrained(atOtherHomes: $atOtherHomes) } } "); context.Prepare(query); // act Rule.Validate(context, query); // assert Assert.Empty(context.Errors); }
protected override ISyntaxVisitorAction Enter( OperationDefinitionNode node, IDocumentValidatorContext context) { context.List.Push(new List <Expression>()); return(base.Enter(node, context)); }
public void UniqueFragments() { // arrange IDocumentValidatorContext context = ValidationUtils.CreateContext(); DocumentNode query = Utf8GraphQLParser.Parse(@" { dog { ...fragmentOne ...fragmentTwo } } fragment fragmentOne on Dog { name } fragment fragmentTwo on Dog { owner { name } } "); context.Prepare(query); // act Rule.Validate(context, query); // assert Assert.Empty(context.Errors); }
public void UndefinedFragment() { // arrange IDocumentValidatorContext context = ValidationUtils.CreateContext(); DocumentNode query = Utf8GraphQLParser.Parse(@" { dog { ...undefinedFragment } } "); context.Prepare(query); // act Rule.Validate(context, query); // assert Assert.Collection(context.Errors, t => Assert.Equal( "The specified fragment `undefinedFragment` " + "does not exist.", t.Message)); context.Errors.MatchSnapshot(); }
public void DefinedFragment() { // arrange IDocumentValidatorContext context = ValidationUtils.CreateContext(); DocumentNode query = Utf8GraphQLParser.Parse(@" { dog { ...definedFragment } } fragment definedFragment on Dog { barkVolume } "); context.Prepare(query); // act Rule.Validate(context, query); // assert Assert.Empty(context.Errors); }
protected override ISyntaxVisitorAction Enter( OperationDefinitionNode node, IDocumentValidatorContext context) { context.Count = 0; return(base.Enter(node, context)); }
protected override ISyntaxVisitorAction Enter( FieldNode node, IDocumentValidatorContext context) { if (IntrospectionFields.TypeName.Equals(node.Name.Value)) { context.Count += _options.ComplexityCalculation.Invoke( TypeNameField, node, null, context.OutputFields.Count + 1, context.Path.Count + 1, GetVariable, _options); return(Skip); } else if (context.Types.TryPeek(out IType type) && type.NamedType() is IComplexOutputType ot && ot.Fields.TryGetField(node.Name.Value, out IOutputField of)) { context.Count += _options.ComplexityCalculation.Invoke( of, node, of.Directives["cost"].FirstOrDefault()?.ToObject <CostDirective>(), context.OutputFields.Count + 1, context.Path.Count + 1, GetVariable, _options); context.OutputFields.Push(of); context.Types.Push(of.Type); return(Continue); }
public static void ExpectValid( ISchema schema, Action <IValidationBuilder> configure, string sourceText) { // arrange var serviceCollection = new ServiceCollection(); IValidationBuilder builder = serviceCollection .AddValidation() .ConfigureValidation(c => c.Modifiers.Add(o => o.Rules.Clear())); configure(builder); IServiceProvider services = serviceCollection.BuildServiceProvider(); var rule = services.GetRequiredService <IValidationConfiguration>() .GetRules("Default").First(); IDocumentValidatorContext context = ValidationUtils.CreateContext(schema); DocumentNode query = Utf8GraphQLParser.Parse(sourceText); context.Prepare(query); // act rule.Validate(context, query); // assert Assert.False(context.UnexpectedErrorsDetected); Assert.Empty(context.Errors); }
public void QueryWithTypeSystemDefinitions() { // arrange IDocumentValidatorContext context = ValidationUtils.CreateContext(); DocumentNode query = Utf8GraphQLParser.Parse(@" query getDogName { dog { name color } } extend type Dog { color: String } "); // act Rule.Validate(context, query); // assert Assert.Collection(context.Errors, t => Assert.Equal( "A document containing TypeSystemDefinition " + "is invalid for execution.", t.Message)); context.Errors.First().MatchSnapshot(); }
protected override ISyntaxVisitorAction Enter( FieldNode node, IDocumentValidatorContext context) { context.Names.Clear(); if (IntrospectionFields.TypeName.Equals(node.Name.Value)) { ValidateArguments( context, node, node.Arguments, TypeNameField.Arguments, field: TypeNameField); return(Skip); } else if (context.Types.TryPeek(out IType type) && type.NamedType() is IComplexOutputType ot && ot.Fields.TryGetField(node.Name.Value, out IOutputField of)) { ValidateArguments( context, node, node.Arguments, of.Arguments, field: of); context.OutputFields.Push(of); context.Types.Push(of.Type); return(Continue); }
protected override ISyntaxVisitorAction Enter( FragmentDefinitionNode node, IDocumentValidatorContext context) { ValidateDirectiveAreUniquePerLocation(node, context); return(Continue); }
protected override ISyntaxVisitorAction Enter( FieldNode node, IDocumentValidatorContext context) { context.Names.Add((node.Alias ?? node.Name).Value); return(Skip); }
protected override ISyntaxVisitorAction Enter( DocumentNode node, IDocumentValidatorContext context) { context.List.Push(new List <OperationComplexityAnalyzer>()); return(base.Enter(node, context)); }
public void QueriesWithValidVariableTypes() { // arrange IDocumentValidatorContext context = ValidationUtils.CreateContext(); DocumentNode query = Utf8GraphQLParser.Parse(@" query takesBoolean($atOtherHomes: Boolean) { dog { isHouseTrained(atOtherHomes: $atOtherHomes) } } query takesComplexInput($complexInput: ComplexInput) { findDog(complex: $complexInput) { name } } query TakesListOfBooleanBang($booleans: [Boolean!]) { booleanList(booleanListArg: $booleans) } "); context.Prepare(query); // act Rule.Validate(context, query); // assert Assert.Empty(context.Errors); }
public void Validate(IDocumentValidatorContext context, DocumentNode document) { if (context is null) { throw new ArgumentNullException(nameof(context)); } if (document is null) { throw new ArgumentNullException(nameof(document)); } IDefinitionNode?typeSystemNode = null; for (int i = 0; i < document.Definitions.Count; i++) { IDefinitionNode node = document.Definitions[i]; if (node.Kind != NodeKind.OperationDefinition && node.Kind != NodeKind.FragmentDefinition) { typeSystemNode = node; break; } } if (typeSystemNode is { })
public void QueryWithSideBySideFragSpreads() { // arrange IDocumentValidatorContext context = ValidationUtils.CreateContext(); DocumentNode query = Utf8GraphQLParser.Parse(@" { dog { ...dogFragment ...dogFragment ...dogFragment ...dogFragment ...dogFragment ...dogFragment ...dogFragment } } fragment dogFragment on Dog { name } "); context.Prepare(query); // act Rule.Validate(context, query); // assert Assert.Empty(context.Errors); }
public void DuplicateFragments() { // arrange IDocumentValidatorContext context = ValidationUtils.CreateContext(); DocumentNode query = Utf8GraphQLParser.Parse(@" { dog { ...fragmentOne } } fragment fragmentOne on Dog { name } fragment fragmentOne on Dog { owner { name } } "); context.Prepare(query); // act Rule.Validate(context, query); // assert Assert.Collection(context.Errors, t => Assert.Equal( "There are multiple fragments with the name `fragmentOne`.", t.Message)); context.Errors.First().MatchSnapshot(); }
public void UnusedFragment() { // arrange IDocumentValidatorContext context = ValidationUtils.CreateContext(); DocumentNode query = Utf8GraphQLParser.Parse(@" fragment nameFragment on Dog { # unused name } { dog { name } } "); context.Prepare(query); // act Rule.Validate(context, query); // assert Assert.Collection(context.Errors, t => Assert.Equal( "The specified fragment `nameFragment` " + "is not used within the current document.", t.Message)); context.Errors.MatchSnapshot(); }
public void OperationWithTwoVariablesThatHaveTheSameName() { // arrange IDocumentValidatorContext context = ValidationUtils.CreateContext(); DocumentNode query = Utf8GraphQLParser.Parse(@" query houseTrainedQuery($atOtherHomes: Boolean, $atOtherHomes: Boolean) { dog { isHousetrained(atOtherHomes: $atOtherHomes) } } "); context.Prepare(query); // act Rule.Validate(context, query); // assert Assert.Single(context.Errors); Assert.Collection(context.Errors, t => Assert.Equal( "A document containing operations that " + "define more than one variable with the same " + "name is invalid for execution.", t.Message)); }
public void UsedNestedFragment() { // arrange IDocumentValidatorContext context = ValidationUtils.CreateContext(); DocumentNode query = Utf8GraphQLParser.Parse(@" fragment nameFragment on Dog { name ... nestedNameFragment } fragment nestedNameFragment on Dog { name } { dog { name ... nameFragment } } "); context.Prepare(query); // act Rule.Validate(context, query); // assert Assert.Empty(context.Errors); }
protected override ISyntaxVisitorAction Enter( VariableNode node, IDocumentValidatorContext context) { context.Used.Add(node.Name.Value); ISyntaxNode parent = context.Path.Peek(); IValueNode?defaultValue = parent.Kind switch { SyntaxKind.Argument => context.InputFields.Peek().DefaultValue, SyntaxKind.ObjectField => context.InputFields.Peek().DefaultValue, _ => null }; if (context.Variables.TryGetValue( node.Name.Value, out VariableDefinitionNode? variableDefinition) && !IsVariableUsageAllowed(variableDefinition, context.Types.Peek(), defaultValue)) { context.Errors.Add(ErrorHelper.VariableIsNotCompatible( context, node, variableDefinition)); } return(Skip); }
public void FragOnUnion() { // arrange IDocumentValidatorContext context = ValidationUtils.CreateContext(); DocumentNode query = Utf8GraphQLParser.Parse(@" { dog { ... fragOnUnion } } fragment fragOnUnion on CatOrDog { ... on Dog { name } } "); context.Prepare(query); // act Rule.Validate(context, query); // assert Assert.Empty(context.Errors); }
protected override ISyntaxVisitorAction Leave( DirectiveNode node, IDocumentValidatorContext context) { context.Directives.Pop(); return(Continue); }
public void FragOnScalar() { // arrange IDocumentValidatorContext context = ValidationUtils.CreateContext(); DocumentNode query = Utf8GraphQLParser.Parse(@" { dog { ... fragOnScalar } } fragment fragOnScalar on Int { something } "); context.Prepare(query); // act Rule.Validate(context, query); // assert Assert.Collection(context.Errors, t => Assert.Equal(t.Message, "Fragments can only be declared on unions, interfaces, " + "and objects.")); context.Errors.MatchSnapshot(); }
protected override ISyntaxVisitorAction Leave( VariableDefinitionNode node, IDocumentValidatorContext context) { context.Types.Pop(); return(base.Enter(node, context)); }
private void ValidateFragmentSpreadIsPossible( ISyntaxNode node, IDocumentValidatorContext context) { if (context.Types.Count > 1 && context.Types.TryPeek(out IType type) && type.IsComplexType()) { INamedType typeCondition = type.NamedType(); INamedType parentType = context.Types[context.Types.Count - 2].NamedType(); if (!IsCompatibleType(parentType, typeCondition)) { context.Errors.Add( ErrorBuilder.New() .SetMessage( "The parent type does not match the type condition on the fragment.") .AddLocation(node) .SetPath(context.CreateErrorPath()) .SetExtension("typeCondition", typeCondition.Visualize()) .SetFragmentName(node) .SpecifiedBy("sec-Fragment-spread-is-possible") .Build()); } } }