Skip to content
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

1131 variables in parse literal for Scalars #1170

Merged
8 changes: 4 additions & 4 deletions src/main/java/graphql/execution/ValuesResolver.java
Original file line number Diff line number Diff line change
Expand Up @@ -246,7 +246,7 @@ private Object coerceValueAst(GraphqlFieldVisibility fieldVisibility, GraphQLTyp
return null;
}
if (type instanceof GraphQLScalarType) {
return parseLiteral(inputValue, ((GraphQLScalarType) type).getCoercing());
return parseLiteral(inputValue, ((GraphQLScalarType) type).getCoercing(), variables);
}
if (isNonNull(type)) {
return coerceValueAst(fieldVisibility, unwrapOne(type), inputValue, variables);
Expand All @@ -255,17 +255,17 @@ private Object coerceValueAst(GraphqlFieldVisibility fieldVisibility, GraphQLTyp
return coerceValueAstForInputObject(fieldVisibility, (GraphQLInputObjectType) type, (ObjectValue) inputValue, variables);
}
if (type instanceof GraphQLEnumType) {
return parseLiteral(inputValue, ((GraphQLEnumType) type).getCoercing());
return parseLiteral(inputValue, ((GraphQLEnumType) type).getCoercing(), variables);
}
if (isList(type)) {
return coerceValueAstForList(fieldVisibility, (GraphQLList) type, inputValue, variables);
}
return null;
}

private Object parseLiteral(Value inputValue, Coercing coercing) {
private Object parseLiteral(Value inputValue, Coercing coercing, Map<String, Object> variables) {
// the CoercingParseLiteralException exception that could happen here has been validated earlier via ValidationUtil
return coercing.parseLiteral(inputValue);
return coercing.parseLiteral(inputValue,variables);
}

private Object coerceValueAstForList(GraphqlFieldVisibility fieldVisibility, GraphQLList graphQLList, Value value, Map<String, Object> variables) {
Expand Down
29 changes: 28 additions & 1 deletion src/main/java/graphql/schema/Coercing.java
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@

import graphql.PublicSpi;

import java.util.Map;

/**
* The Coercing interface is used by {@link graphql.schema.GraphQLScalarType}s to parse and serialise object values.
* <p>
Expand Down Expand Up @@ -50,7 +52,7 @@ public interface Coercing<I, O> {
I parseValue(Object input) throws CoercingParseValueException;

/**
* Called to convert an query input AST node into a Java object acceptable for the scalar type. The input
* Called during query validation to convert an query input AST node into a Java object acceptable for the scalar type. The input
* object will be an instance of {@link graphql.language.Value}.
*
* Note : You should not allow {@link java.lang.RuntimeException}s to come out of your parseLiteral method, but rather
Expand All @@ -63,4 +65,29 @@ public interface Coercing<I, O> {
* @throws graphql.schema.CoercingParseLiteralException if input literal can't be parsed
*/
I parseLiteral(Object input) throws CoercingParseLiteralException;

/**
* Called during query execution to convert an query input AST node into a Java object acceptable for the scalar type. The input
* object will be an instance of {@link graphql.language.Value}.
*
* Note : You should not allow {@link java.lang.RuntimeException}s to come out of your parseLiteral method, but rather
* catch them and fire them as {@link graphql.schema.CoercingParseLiteralException} instead as per the method contract.
*
* Many scalar types don't need to implement this method because they do'nt take AST {@link graphql.language.VariableReference}
* objects and convert them into actual values. But for those scalar types that want to do this, then this
* method should be implemented.
*
* @param input is never null
* @param variables the resolved variables passed to the query
*
* @return a parsed value which is never null
*
* @throws graphql.schema.CoercingParseLiteralException if input literal can't be parsed
*/
@SuppressWarnings("unused")
default I parseLiteral(Object input, Map<String, Object> variables) throws CoercingParseLiteralException {
return parseLiteral(input);
}

;
}
118 changes: 118 additions & 0 deletions src/test/groovy/graphql/schema/CoercingTest.groovy
Original file line number Diff line number Diff line change
@@ -0,0 +1,118 @@
package graphql.schema

import graphql.ExecutionInput
import graphql.GraphQL
import graphql.TestUtil
import graphql.language.ArrayValue
import graphql.language.BooleanValue
import graphql.language.FloatValue
import graphql.language.IntValue
import graphql.language.NullValue
import graphql.language.ObjectValue
import graphql.language.StringValue
import graphql.language.VariableReference
import graphql.schema.idl.RuntimeWiring
import graphql.schema.idl.TypeRuntimeWiring
import spock.lang.Specification

class CoercingTest extends Specification {

GraphQLScalarType mapLikeScalar = new GraphQLScalarType("MapLike", "MapLike", new Coercing() {
@Override
Object serialize(Object dataFetcherResult) throws CoercingSerializeException {
return dataFetcherResult
}

@Override
Object parseValue(Object input) throws CoercingParseValueException {
return input
}

@Override
Object parseLiteral(Object input) throws CoercingParseLiteralException {
return parseLiteral(input, [:])
}

@Override
Object parseLiteral(Object input, Map variables) throws CoercingParseLiteralException {
if (input instanceof StringValue) {
return ((StringValue) input).getValue()
}
if (input instanceof IntValue) {
return ((IntValue) input).getValue()
}
if (input instanceof FloatValue) {
return ((FloatValue) input).getValue()
}
if (input instanceof BooleanValue) {
return ((BooleanValue) input).isValue()
}
if (input instanceof ObjectValue) {
Map<String, Object> obj = new LinkedHashMap<>()
((ObjectValue) input).getObjectFields().forEach({
fld ->
def value = parseLiteral(fld.getValue(), variables)
obj.put(fld.getName(), value)
})
return obj
}
if (input instanceof VariableReference) {
def name = ((VariableReference) input).getName()
return variables.get(name)
}
if (input instanceof ArrayValue) {
return ((ArrayValue) input).getValues().collect({ v -> parseLiteral(v, variables) })
}
if (input instanceof NullValue) {
return null
}
throw new CoercingParseLiteralException()
}
})


def "end to end test of coercing with variables references"() {
def spec = '''

scalar MapLike

type Query {
field(argument : MapLike) : MapLike
}
'''
DataFetcher df = new DataFetcher() {
@Override
Object get(DataFetchingEnvironment environment) {
def argument = environment.getArgument("argument")
return argument
}
}

def runtimeWiring = RuntimeWiring.newRuntimeWiring()
.type(TypeRuntimeWiring.newTypeWiring("Query")
.dataFetcher("field", df))
.scalar(mapLikeScalar)
.build()


def schema = TestUtil.schema(spec, runtimeWiring)
def graphQL = GraphQL.newGraphQL(schema).build()
def executionInput = ExecutionInput.newExecutionInput()
.variables([
strVar: "strVar",
intVar: 999
])
.query('''
query Q($strVar : String) {
field(argument : { s : $strVar, i : 666 })
}
''')
.build()

when:
def er = graphQL.execute(executionInput)
then:
er.errors.isEmpty()
er.data == [field: [s: "strVar", i: 666]]
}
}