diff --git a/src/main/java/graphql/validation/rules/VariableTypesMatchRule.java b/src/main/java/graphql/validation/rules/VariableTypesMatchRule.java index 96c09509b5..2d3c7c773d 100644 --- a/src/main/java/graphql/validation/rules/VariableTypesMatchRule.java +++ b/src/main/java/graphql/validation/rules/VariableTypesMatchRule.java @@ -3,12 +3,15 @@ import graphql.Internal; import graphql.execution.TypeFromAST; +import graphql.execution.ValuesResolver; import graphql.language.OperationDefinition; +import graphql.language.Value; import graphql.language.VariableDefinition; import graphql.language.VariableReference; import graphql.schema.GraphQLInputType; import graphql.schema.GraphQLType; import graphql.schema.GraphQLTypeUtil; +import graphql.schema.InputValueWithState; import graphql.validation.AbstractRule; import graphql.validation.ValidationContext; import graphql.validation.ValidationErrorCollector; @@ -16,6 +19,7 @@ import java.util.LinkedHashMap; import java.util.Map; +import java.util.Optional; @Internal public class VariableTypesMatchRule extends AbstractRule { @@ -55,11 +59,19 @@ public void checkVariable(VariableReference variableReference) { return; } GraphQLInputType expectedType = getValidationContext().getInputType(); + Optional schemaDefault = Optional.ofNullable(getValidationContext().getArgument()).map(v -> v.getArgumentDefaultValue()); + Value schemaDefaultValue = null; + if (schemaDefault.isPresent() && schemaDefault.get().isLiteral()) { + schemaDefaultValue = (Value) schemaDefault.get().getValue(); + } else if (schemaDefault.isPresent() && schemaDefault.get().isSet()) { + schemaDefaultValue = ValuesResolver.valueToLiteral(schemaDefault.get(), expectedType); + } if (expectedType == null) { // we must have a unknown variable say to not have a known type return; } - if (!variablesTypesMatcher.doesVariableTypesMatch(variableType, variableDefinition.getDefaultValue(), expectedType)) { + if (!variablesTypesMatcher.doesVariableTypesMatch(variableType, variableDefinition.getDefaultValue(), expectedType) && + !variablesTypesMatcher.doesVariableTypesMatch(variableType, schemaDefaultValue, expectedType)) { GraphQLType effectiveType = variablesTypesMatcher.effectiveType(variableType, variableDefinition.getDefaultValue()); String message = String.format("Variable type '%s' doesn't match expected type '%s'", GraphQLTypeUtil.simplePrint(effectiveType), diff --git a/src/test/groovy/graphql/GraphQLTest.groovy b/src/test/groovy/graphql/GraphQLTest.groovy index 323d75bd9c..a29222f296 100644 --- a/src/test/groovy/graphql/GraphQLTest.groovy +++ b/src/test/groovy/graphql/GraphQLTest.groovy @@ -1129,6 +1129,26 @@ many lines'''] } + def "default value defined in the schema is used when none provided in the query"() { + // Spec (https://spec.graphql.org/June2018/#sec-All-Variable-Usages-are-Allowed): A notable exception to typical variable type compatibility is allowing a variable definition with a nullable type to be provided to a non‐null location as long as either that variable or that location provides a default value. + given: + def spec = """type Query { + sayHello(name: String! = "amigo"): String + }""" + def df = { dfe -> + return dfe.getArgument("name") + } as DataFetcher + def graphQL = TestUtil.graphQL(spec, ["Query": ["sayHello": df]]).build() + + when: + def result = graphQL.execute('query($var:String){sayHello(name:$var)}'); + + then: + result.errors.isEmpty() + result.getData() == [sayHello: "amigo"] + + } + def "specified url can be defined and queried via introspection"() { given: GraphQLSchema schema = TestUtil.schema('type Query {foo: MyScalar} scalar MyScalar @specifiedBy(url:"myUrl")'); diff --git a/src/test/groovy/graphql/validation/rules/VariableTypesMatchRuleTest.groovy b/src/test/groovy/graphql/validation/rules/VariableTypesMatchRuleTest.groovy index 0b01047155..743dd03e7b 100644 --- a/src/test/groovy/graphql/validation/rules/VariableTypesMatchRuleTest.groovy +++ b/src/test/groovy/graphql/validation/rules/VariableTypesMatchRuleTest.groovy @@ -9,6 +9,7 @@ import graphql.language.StringValue import graphql.language.TypeName import graphql.language.VariableDefinition import graphql.language.VariableReference +import graphql.schema.GraphQLArgument import graphql.schema.GraphQLList import graphql.schema.GraphQLNonNull import graphql.validation.ValidationContext @@ -33,9 +34,11 @@ class VariableTypesMatchRuleTest extends Specification { def defaultValue = new StringValue("default") def astType = new TypeName("String") def expectedType = Scalars.GraphQLBoolean + def argument = GraphQLArgument.newArgument().name("test").type(expectedType).build(); validationContext.getSchema() >> StarWarsSchema.starWarsSchema validationContext.getInputType() >> expectedType + validationContext.getArgument() >> argument variablesTypeMatcher.effectiveType(Scalars.GraphQLString, defaultValue) >> Scalars.GraphQLString variablesTypeMatcher .doesVariableTypesMatch(Scalars.GraphQLString, defaultValue, expectedType) >> false @@ -54,11 +57,13 @@ class VariableTypesMatchRuleTest extends Specification { def defaultValue = null def astType = new NonNullType(new ListType(new TypeName("String"))) def expectedType = GraphQLList.list(GraphQLNonNull.nonNull(Scalars.GraphQLString)) + def argument = GraphQLArgument.newArgument().name("test").type(expectedType).build(); def mismatchedType = GraphQLNonNull.nonNull(GraphQLList.list(Scalars.GraphQLString)) validationContext.getSchema() >> StarWarsSchema.starWarsSchema validationContext.getInputType() >> expectedType + validationContext.getArgument() >> argument variablesTypeMatcher.effectiveType({ mismatchedType.isEqualTo(it) }, defaultValue) >> mismatchedType variablesTypeMatcher .doesVariableTypesMatch(expectedType, defaultValue, expectedType) >> false