Skip to content

port of 8bf256551c054f97c63171fe8f934ee715e9436b to stable #35

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ public boolean appliesTo(GraphQLFieldDefinition fieldDefinition, GraphQLFieldsCo
@Override
public boolean appliesTo(GraphQLArgument argument, GraphQLFieldDefinition fieldDefinition, GraphQLFieldsContainer fieldsContainer) {

boolean suitable = DirectivesAndTypeWalker.isSuitable(argument, (inputType, directive) -> {
boolean suitable = new DirectivesAndTypeWalker().isSuitable(argument, (inputType, directive) -> {
boolean hasNamedDirective = directive.getName().equals(this.getName());
if (hasNamedDirective) {
inputType = Util.unwrapNonNull(inputType);
Expand Down
28 changes: 22 additions & 6 deletions src/main/java/graphql/validation/util/DirectivesAndTypeWalker.java
Original file line number Diff line number Diff line change
Expand Up @@ -8,34 +8,45 @@
import graphql.schema.GraphQLInputObjectType;
import graphql.schema.GraphQLInputType;
import graphql.schema.GraphQLList;
import graphql.schema.GraphQLTypeUtil;

import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.function.BiFunction;

@Internal
public class DirectivesAndTypeWalker {

public static boolean isSuitable(GraphQLArgument argument, BiFunction<GraphQLInputType, GraphQLDirective, Boolean> isSuitable) {
private final Map<String, Boolean> seenTypes = new HashMap<>();

public boolean isSuitable(GraphQLArgument argument, BiFunction<GraphQLInputType, GraphQLDirective, Boolean> isSuitable) {
GraphQLInputType inputType = argument.getType();
List<GraphQLDirective> directives = argument.getDirectives();
return walkInputType(inputType, directives, isSuitable);
}

private static boolean walkInputType(GraphQLInputType inputType, List<GraphQLDirective> directives, BiFunction<GraphQLInputType, GraphQLDirective, Boolean> isSuitable) {
private boolean walkInputType(GraphQLInputType inputType, List<GraphQLDirective> directives, BiFunction<GraphQLInputType, GraphQLDirective, Boolean> isSuitable) {
String typeName = GraphQLTypeUtil.unwrapAll(inputType).getName();
GraphQLInputType unwrappedInputType = Util.unwrapNonNull(inputType);
for (GraphQLDirective directive : directives) {
if (isSuitable.apply(unwrappedInputType, directive)) {
return true;
return seen(typeName,true);
}
}
if (unwrappedInputType instanceof GraphQLInputObjectType) {
GraphQLInputObjectType inputObjType = (GraphQLInputObjectType) unwrappedInputType;
if (seenTypes.containsKey(typeName)) {
return seenTypes.get(typeName);
}
seen(typeName,false);

for (GraphQLInputObjectField inputField : inputObjType.getFieldDefinitions()) {
inputType = inputField.getType();
directives = inputField.getDirectives();

if (walkInputType(inputType, directives, isSuitable)) {
return true;
return seen(typeName,true);
}
}
}
Expand All @@ -44,11 +55,16 @@ private static boolean walkInputType(GraphQLInputType inputType, List<GraphQLDir
if (innerListType instanceof GraphQLDirectiveContainer) {
directives = ((GraphQLDirectiveContainer) innerListType).getDirectives();
if (walkInputType(innerListType, directives, isSuitable)) {
return true;
return seen(typeName,true);
}
}
}
return false;
return seen(typeName,false);
}

private boolean seen(String typeName, boolean flag) {
seenTypes.put(typeName, flag);
return flag;
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,11 @@ package graphql.validation.rules
import graphql.GraphQL
import graphql.execution.DataFetcherResult
import graphql.schema.DataFetcher
import graphql.schema.DataFetchingEnvironment
import graphql.schema.idl.RuntimeWiring
import graphql.validation.TestUtil
import graphql.validation.constraints.DirectiveConstraints
import graphql.validation.schemawiring.ValidationSchemaWiring
import spock.lang.Specification

import static graphql.schema.idl.TypeRuntimeWiring.newTypeWiring
Expand Down Expand Up @@ -78,4 +80,74 @@ class ValidationRulesTest extends Specification {
er.errors[2].message == "/cars/filter/model size must be between 0 and 10"
er.errors[2].path == ["cars"]
}

def "issue 17 - type references handled"() {

def directiveRules = DirectiveConstraints.newDirectiveConstraints().build()

def sdl = '''

''' + directiveRules.directivesSDL + '''

input NameRequest {
# The title associated to the name
title: String @Size(min : 1, max : 1)
# The given name
givenName: String! @Size(min : 1, max : 1)
# Middle Name
middleName: String
# Last Name
surName: String!

# recursion
inner : NameRequest

innerList : [NameRequest!]
}

type Query {
request( arg : NameRequest!) : String
}
'''

ValidationRules validationRules = ValidationRules.newValidationRules()
.onValidationErrorStrategy(OnValidationErrorStrategy.RETURN_NULL).build()
def validationWiring = new ValidationSchemaWiring(validationRules)

DataFetcher df = { DataFetchingEnvironment env ->
return "OK"
}

def runtime = RuntimeWiring.newRuntimeWiring()
.type(newTypeWiring("Query").dataFetcher("request", df))
.directiveWiring(validationWiring)
.build()
def graphQLSchema = TestUtil.schema(sdl, runtime)
def graphQL = GraphQL.newGraphQL(graphQLSchema).build()

when:

def er = graphQL.execute('''
{
request (
arg : {
title : "Mr BRAD", givenName : "BRADLEY" , surName : "BAKER"
inner : { title : "Mr BRAD", givenName : "BRADLEY" , surName : "BAKER" }
innerList : [ { title : "Mr BRAD", givenName : "BRADLEY" , surName : "BAKER" } ]
}
)
}
''')
then:
er != null
er.data["request"] == null
er.errors.size() == 6

er.errors[0].getMessage() == "/request/arg/givenName size must be between 1 and 1"
er.errors[1].getMessage() == "/request/arg/inner/givenName size must be between 1 and 1"
er.errors[2].getMessage() == "/request/arg/inner/title size must be between 1 and 1"
er.errors[3].getMessage() == "/request/arg/innerList[0]/givenName size must be between 1 and 1"
er.errors[4].getMessage() == "/request/arg/innerList[0]/title size must be between 1 and 1"
er.errors[5].getMessage() == "/request/arg/title size must be between 1 and 1"
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
package graphql.validation.util


import graphql.schema.GraphQLObjectType
import graphql.validation.TestUtil
import spock.lang.Specification

class DirectivesAndTypeWalkerTest extends Specification {

def "can walk self referencing types"() {
def sdl = """
input TestInput @deprecated {
name : String
list : [TestInput]
self : TestInput
}

type Query {
f(arg : TestInput) : String
}
"""

def schema = TestUtil.schema(sdl)
def arg = (schema.getType("Query") as GraphQLObjectType).getFieldDefinition("f").getArgument("arg")
def callback = { t, d -> true }
when:
def suitable = new DirectivesAndTypeWalker().isSuitable(arg, callback)

then:
suitable
}
}