-
Notifications
You must be signed in to change notification settings - Fork 1.2k
Description
The QA team here is asking me about the validation of Boolean input values.
I need to give them an answer.
The GraphQL specification for input Boolean and String values states:
Reference: http://facebook.github.io/graphql/October2016/#sec-Boolean-Value
2.9.3 Boolean Value
BooleanValue: one of
true false
The two keywords true and false represent the two boolean values.
2.9.4 String Value
StringValue ::
""
" StringCharacter list "
[excerpted]
Strings are sequences of characters wrapped in double‐quotes ("). (ex. "Hello World").
White space and other otherwise‐ignored characters are significant within a string value.
Is this the reference specification used for the graphql-java implementation?
The same or a similar issue was recently addressed in graphql-js here: graphql/graphql-js#771
Example follows. The output is generated by the graphql java 6.0 release.
Schema Definition:
schema { query : Query }
type Query { ping ( input : Param ! ) : Ping }
input Param {
s : String !
b : Boolean !
}
type Ping {
s : String
b : Boolean
}
Sample requests and program output:
R-1:
{
"query" : "query ( $input : Param ! ) { ping ( input : $input ) { s b }}"
"variables" : {
"input" : {
"s" : 0
"b" : 0
}
}
}
Output: {"data":{"ping":{"s":"0","b":false}}}
R-2:
{
"query" : "query ( $input : Param ! ) { ping ( input : $input ) { s b }}"
"variables" : {
"input" : {
"s" : 123345,
"b" : 123345
}
}
}
Output: {"data":{"ping":{"s":"12345","b":true}}}
R-3:
{
"query" : "query ( $input : Param ! ) { ping ( input : $input ) { s b } }"
"variables" : {
"input" : {
"s" : "Atlanta",
"b" : "Atlanta"
}
}
}
Output: {"data":{"ping":{"s":"Atlanta","b":false}}}
R-4:
{
"query" : "query ( $input : Param ! ) { ping ( input : $input ) { s b } }"
"variables" : {
"input" : {
"s" : [1,2,3],
"b" : "Atlanta"
}
}
}
Output: {"data":{"ping":{"s":"[1, 2, 3]","b":false}}}
R-5:
{
"query" : "query ( $input : Param ! ) { ping ( input : $input ) { s b } }"
"variables" : {
"input" : {
"s" : [1,2,3],
"b" : [1,2,3]
}
}
}
Output: {"errors":[{"message":"Invalid input '[1, 2, 3]' for Boolean"}]}
Output Expectations:
R-1, R-2 should return an error for either s or b because the value of s is not quoted and the value of b is neither true nor false.
R-3 should return return an error for b because the value of b is neither true nor false.
R-4 should return an error for s because the value of s is not quoted
R-5 is ok because an error is returned for b as expected; but depending on the order of validation an error for s might be returned instead.
The Java 8 Program used for testing:
package graphqltest;
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.JsonSerializer;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializerProvider;
import com.fasterxml.jackson.databind.module.SimpleModule;
import graphql.ExecutionInput;
import graphql.ExecutionResult;
import graphql.GraphQL;
import graphql.schema.GraphQLSchema;
import graphql.schema.idl.RuntimeWiring;
import graphql.schema.idl.SchemaGenerator;
import graphql.schema.idl.SchemaParser;
import graphql.schema.idl.TypeDefinitionRegistry;
import graphql.schema.idl.TypeInfo;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
public class GraphQLTest {
public static void main(String[] args) {
SchemaParser parser = new SchemaParser();
SchemaGenerator generator = new SchemaGenerator();
TypeDefinitionRegistry registry = parser.parse("schema { query : Query } type Query { ping ( input : Param ! ) : Ping } input Param { s : String ! b : Boolean ! } type Ping{ s : String b : Boolean }");
RuntimeWiring wiring = RuntimeWiring.newRuntimeWiring().type("Query", wires -> wires.dataFetcher("ping", env -> {
if (env.containsArgument("input")) { return env.getArgument("input"); }
return null;
})).build();
GraphQLSchema schema = generator.makeExecutableSchema(registry, wiring);
GraphQL gql = GraphQL.newGraphQL(schema).build();
ObjectMapper mapper = new ObjectMapper();
SimpleModule mod = new SimpleModule();
mod.addSerializer(TypeInfo.class, new JsonSerializer<TypeInfo>() {
@Override
public void serialize(TypeInfo t, JsonGenerator jg, SerializerProvider sp) throws IOException, JsonProcessingException { jg.writeString(t.toString()); }
});
mapper.registerModule(mod);
HashMapTypeReference typeRef = new HashMapTypeReference();
String[] requests = new String[]{
"{\"query\":\"query ( $input : Param ! ) { ping ( input : $input ) { s b }}\",\"variables\":{\"input\":{\"s\":0,\"b\":0}}}", // R-1
"{\"query\":\"query ( $input : Param ! ) { ping ( input : $input ) { s b }}\",\"variables\":{\"input\":{\"s\":12345,\"b\":12345}}}", // R-2
"{\"query\":\"query ( $input : Param ! ) { ping ( input : $input ) { s b }}\",\"variables\":{\"input\":{\"s\":\"Atlanta\",\"b\":\"Atlanta\"}}}", // R-3
"{\"query\":\"query ( $input : Param ! ) { ping ( input : $input ) { s b }}\",\"variables\":{\"input\":{\"s\":[1,2,3],\"b\":\"Atlanta\"}}}", // R-4
"{\"query\":\"query ( $input : Param ! ) { ping ( input : $input ) { s b }}\",\"variables\":{\"input\":{\"s\":[1,2,3],\"b\":[1,2,3]}}}" // R-5
};
for (String request : requests) {
try {
Map<String, Object> input = mapper.readValue(request, typeRef);
String query = (String) input.get("query");
Map<String, Object> variables = (Map<String, Object>) input.get("variables");
ExecutionResult result = gql.execute(ExecutionInput.newExecutionInput().query(query).variables(variables).build());
System.out.println("----------");
System.out.println("Request: " + request);
System.out.println(" Output: " + mapper.writeValueAsString(result.toSpecification()));
} catch (Exception ex) {
System.out.println(ex);
}
}
System.out.append("----------");
}
@SuppressWarnings("PublicInnerClass")
public static class HashMapTypeReference extends TypeReference<HashMap<String, Object>> { }
}
Libraries:
antlr-runtime-4.7.jar
graphql-java-6.0.jar
jackson-annotations-2.9.2.jar
jackson-core-2.9.2.jar
jackson-databind-2.9.2.jar
java-dataloader-2.0.1.jar
log4j-api-2.8.2.jar
log4j-core-2.8.2.jar
log4j-slf4j-impl-2.8.2.jar
slf4j-api-1.7.25.jar