Skip to content

Commit 0deb127

Browse files
authored
1131 variables in parse literal for Scalars (graphql-java#1170)
* Added @OverRide as part of errorprone code health check * Revert "Added @OverRide as part of errorprone code health check" This reverts commit 38dfab1 * Added the ability for Scalars to get the variables map, just like in graphql-js
1 parent b66eade commit 0deb127

3 files changed

Lines changed: 150 additions & 5 deletions

File tree

src/main/java/graphql/execution/ValuesResolver.java

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -246,7 +246,7 @@ private Object coerceValueAst(GraphqlFieldVisibility fieldVisibility, GraphQLTyp
246246
return null;
247247
}
248248
if (type instanceof GraphQLScalarType) {
249-
return parseLiteral(inputValue, ((GraphQLScalarType) type).getCoercing());
249+
return parseLiteral(inputValue, ((GraphQLScalarType) type).getCoercing(), variables);
250250
}
251251
if (isNonNull(type)) {
252252
return coerceValueAst(fieldVisibility, unwrapOne(type), inputValue, variables);
@@ -255,17 +255,17 @@ private Object coerceValueAst(GraphqlFieldVisibility fieldVisibility, GraphQLTyp
255255
return coerceValueAstForInputObject(fieldVisibility, (GraphQLInputObjectType) type, (ObjectValue) inputValue, variables);
256256
}
257257
if (type instanceof GraphQLEnumType) {
258-
return parseLiteral(inputValue, ((GraphQLEnumType) type).getCoercing());
258+
return parseLiteral(inputValue, ((GraphQLEnumType) type).getCoercing(), variables);
259259
}
260260
if (isList(type)) {
261261
return coerceValueAstForList(fieldVisibility, (GraphQLList) type, inputValue, variables);
262262
}
263263
return null;
264264
}
265265

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

271271
private Object coerceValueAstForList(GraphqlFieldVisibility fieldVisibility, GraphQLList graphQLList, Value value, Map<String, Object> variables) {

src/main/java/graphql/schema/Coercing.java

Lines changed: 28 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@
33

44
import graphql.PublicSpi;
55

6+
import java.util.Map;
7+
68
/**
79
* The Coercing interface is used by {@link graphql.schema.GraphQLScalarType}s to parse and serialise object values.
810
* <p>
@@ -50,7 +52,7 @@ public interface Coercing<I, O> {
5052
I parseValue(Object input) throws CoercingParseValueException;
5153

5254
/**
53-
* Called to convert an query input AST node into a Java object acceptable for the scalar type. The input
55+
* Called during query validation to convert an query input AST node into a Java object acceptable for the scalar type. The input
5456
* object will be an instance of {@link graphql.language.Value}.
5557
*
5658
* Note : You should not allow {@link java.lang.RuntimeException}s to come out of your parseLiteral method, but rather
@@ -63,4 +65,29 @@ public interface Coercing<I, O> {
6365
* @throws graphql.schema.CoercingParseLiteralException if input literal can't be parsed
6466
*/
6567
I parseLiteral(Object input) throws CoercingParseLiteralException;
68+
69+
/**
70+
* Called during query execution to convert an query input AST node into a Java object acceptable for the scalar type. The input
71+
* object will be an instance of {@link graphql.language.Value}.
72+
*
73+
* Note : You should not allow {@link java.lang.RuntimeException}s to come out of your parseLiteral method, but rather
74+
* catch them and fire them as {@link graphql.schema.CoercingParseLiteralException} instead as per the method contract.
75+
*
76+
* Many scalar types don't need to implement this method because they do'nt take AST {@link graphql.language.VariableReference}
77+
* objects and convert them into actual values. But for those scalar types that want to do this, then this
78+
* method should be implemented.
79+
*
80+
* @param input is never null
81+
* @param variables the resolved variables passed to the query
82+
*
83+
* @return a parsed value which is never null
84+
*
85+
* @throws graphql.schema.CoercingParseLiteralException if input literal can't be parsed
86+
*/
87+
@SuppressWarnings("unused")
88+
default I parseLiteral(Object input, Map<String, Object> variables) throws CoercingParseLiteralException {
89+
return parseLiteral(input);
90+
}
91+
92+
;
6693
}
Lines changed: 118 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,118 @@
1+
package graphql.schema
2+
3+
import graphql.ExecutionInput
4+
import graphql.GraphQL
5+
import graphql.TestUtil
6+
import graphql.language.ArrayValue
7+
import graphql.language.BooleanValue
8+
import graphql.language.FloatValue
9+
import graphql.language.IntValue
10+
import graphql.language.NullValue
11+
import graphql.language.ObjectValue
12+
import graphql.language.StringValue
13+
import graphql.language.VariableReference
14+
import graphql.schema.idl.RuntimeWiring
15+
import graphql.schema.idl.TypeRuntimeWiring
16+
import spock.lang.Specification
17+
18+
class CoercingTest extends Specification {
19+
20+
GraphQLScalarType mapLikeScalar = new GraphQLScalarType("MapLike", "MapLike", new Coercing() {
21+
@Override
22+
Object serialize(Object dataFetcherResult) throws CoercingSerializeException {
23+
return dataFetcherResult
24+
}
25+
26+
@Override
27+
Object parseValue(Object input) throws CoercingParseValueException {
28+
return input
29+
}
30+
31+
@Override
32+
Object parseLiteral(Object input) throws CoercingParseLiteralException {
33+
return parseLiteral(input, [:])
34+
}
35+
36+
@Override
37+
Object parseLiteral(Object input, Map variables) throws CoercingParseLiteralException {
38+
if (input instanceof StringValue) {
39+
return ((StringValue) input).getValue()
40+
}
41+
if (input instanceof IntValue) {
42+
return ((IntValue) input).getValue()
43+
}
44+
if (input instanceof FloatValue) {
45+
return ((FloatValue) input).getValue()
46+
}
47+
if (input instanceof BooleanValue) {
48+
return ((BooleanValue) input).isValue()
49+
}
50+
if (input instanceof ObjectValue) {
51+
Map<String, Object> obj = new LinkedHashMap<>()
52+
((ObjectValue) input).getObjectFields().forEach({
53+
fld ->
54+
def value = parseLiteral(fld.getValue(), variables)
55+
obj.put(fld.getName(), value)
56+
})
57+
return obj
58+
}
59+
if (input instanceof VariableReference) {
60+
def name = ((VariableReference) input).getName()
61+
return variables.get(name)
62+
}
63+
if (input instanceof ArrayValue) {
64+
return ((ArrayValue) input).getValues().collect({ v -> parseLiteral(v, variables) })
65+
}
66+
if (input instanceof NullValue) {
67+
return null
68+
}
69+
throw new CoercingParseLiteralException()
70+
}
71+
})
72+
73+
74+
def "end to end test of coercing with variables references"() {
75+
def spec = '''
76+
77+
scalar MapLike
78+
79+
type Query {
80+
field(argument : MapLike) : MapLike
81+
}
82+
'''
83+
DataFetcher df = new DataFetcher() {
84+
@Override
85+
Object get(DataFetchingEnvironment environment) {
86+
def argument = environment.getArgument("argument")
87+
return argument
88+
}
89+
}
90+
91+
def runtimeWiring = RuntimeWiring.newRuntimeWiring()
92+
.type(TypeRuntimeWiring.newTypeWiring("Query")
93+
.dataFetcher("field", df))
94+
.scalar(mapLikeScalar)
95+
.build()
96+
97+
98+
def schema = TestUtil.schema(spec, runtimeWiring)
99+
def graphQL = GraphQL.newGraphQL(schema).build()
100+
def executionInput = ExecutionInput.newExecutionInput()
101+
.variables([
102+
strVar: "strVar",
103+
intVar: 999
104+
])
105+
.query('''
106+
query Q($strVar : String) {
107+
field(argument : { s : $strVar, i : 666 })
108+
}
109+
''')
110+
.build()
111+
112+
when:
113+
def er = graphQL.execute(executionInput)
114+
then:
115+
er.errors.isEmpty()
116+
er.data == [field: [s: "strVar", i: 666]]
117+
}
118+
}

0 commit comments

Comments
 (0)