Raw and Coerced Variables refactor#2802
Conversation
| this.graphQLContext = assertNotNull(builder.graphQLContext); | ||
| this.root = builder.root; | ||
| this.variables = builder.variables; | ||
| this.rawVariables = builder.rawVariables; |
There was a problem hiding this comment.
Variables in ExecutionInput are always raw
| this.roots = singletonList(getOperationResult.operationDefinition); | ||
| this.rootParentType = getRootTypeFromOperation(getOperationResult.operationDefinition); | ||
| this.variables = coerceVariables(assertNotNull(variables, () -> "variables can't be null"), variableDefinitions); | ||
| this.coercedVariables = coerceVariables(assertNotNull(variables, () -> "variables can't be null"), variableDefinitions); |
There was a problem hiding this comment.
For now, keep functionality similar to before
In the next PR (#2773), QueryTraverser will change to accept already coerced variables
| */ | ||
| @Internal | ||
| public class CoercedVariables { | ||
| private final ImmutableMapWithNullValues<String, Object> coercedVariables; |
There was a problem hiding this comment.
Using this special map as variable values can be null
| private final OperationDefinition operationDefinition; | ||
| private final Document document; | ||
| private final ImmutableMapWithNullValues<String, Object> variables; | ||
| private final CoercedVariables coercedVariables; |
There was a problem hiding this comment.
ExecutionContext can only contain coerced variables. This change makes it clearer
| */ | ||
| @Internal | ||
| public class RawVariables { | ||
| private final ImmutableMapWithNullValues<String, Object> rawVariables; |
There was a problem hiding this comment.
Using this special map as values can be null
| def directives = [Directive.newDirective().name("skip").arguments([argument]).build()] | ||
|
|
||
| conditionalNodes.valuesResolver.getArgumentValues(Directives.SkipDirective.getArguments(), [argument], variables) >> ["if": true] | ||
|
|
There was a problem hiding this comment.
This mock wasn't necessary
| def resolvedValues = resolver.coerceVariableValues(schema, [variableDefinition], new RawVariables([variable: inputValue])) | ||
| then: | ||
| resolvedValues['variable'] == outputValue | ||
| resolvedValues.get('variable') == outputValue |
There was a problem hiding this comment.
Most API changes in this test file
| Document document; | ||
| OperationDefinition operationDefinition; | ||
| ImmutableMapWithNullValues<String, Object> variables = ImmutableMapWithNullValues.emptyMap(); | ||
| CoercedVariables coercedVariables = new CoercedVariables(Collections.emptyMap()); |
There was a problem hiding this comment.
how about CoercedVariables.emptyVariables() - reads nicer - not essential
| this.coercedVariables = ImmutableMapWithNullValues.copyOf(coercedVariables); | ||
| } | ||
|
|
||
| public Map<String, Object> getMap() { |
There was a problem hiding this comment.
map name it toMap that is you are converting it from a X -> Y - from a CoercedVariables to a map representtion
| executionInput.locale == Locale.GERMAN | ||
| executionInput.extensions == [some: "map"] | ||
| executionInput.query == "new query" | ||
| } |
There was a problem hiding this comment.
What happens if we set both
.rawVariables(x).variables(y)
Should we have a test for this. Do they go into the same place?
There was a problem hiding this comment.
I'll add a test.
Yes, in ExecutionInput both rawVariables and variables set the same field. If both get set, the last one would be used (in your example y).
Is it worthwhile preventing both .rawVariables(x).variables(y) to be used? I don't mind adding another check if it'll be clearer
bbakerman
left a comment
There was a problem hiding this comment.
I generally appove strongly of this
See my specific comments on naming.
bbakerman
left a comment
There was a problem hiding this comment.
As we discussed in person - lets remove the RawVariables fron ExecutionInput signature but use it on the inside
# Conflicts: # src/main/java/graphql/ExecutionInput.java # src/main/java/graphql/execution/ValuesResolver.java # src/main/java/graphql/normalized/ExecutableNormalizedOperationFactory.java
Why refactor? Identifying double coercion
Variables were previously represented as a
Map<String, Object>, which made it difficult to determine if variables were raw or had been already coerced.Example: double coercion in
FieldValidationInstrumentationLet's start from this line in
Execution#executeOperation. At this point, variables have already been coerced. TheExecutionContextcan only contain coerced variables.Then inside
FieldValidationInstrumentation#beginExecuteOperationwe haveWithin
validateFieldsAndArguments, we create aQueryTraverser, which coerces variables a second time.Example: double coercion in WIP PR to shift max query depth after validation
https://github.com/graphql-java/graphql-java/pull/2773/files#diff-2696e88b9e4d095eea4d0e47b1c98ea872bfce3642c5aaee7317b1c3c2c478a5R149
This is a WIP PR so I don't want to be harsh, nevertheless it's a good illustration of how confusing raw and coerced variables are. The
ExecutionContextcan only contain coerced variables. The linkedQueryTraverserwill coerce variables a second time.This PR adds
RawVariablesandCoercedVariablesThis PR adds
RawVariablesandCoercedVariablesto make the distinction clearer and avoid accidental double coercion.This PR addresses #2764, and sets the foundation for #2773.
This is a large PR! To make it easier to see the changes, please read commit by commit (or alternatively, I can split this work into multiple PRs).
What did change
I refactored variables to
RawVariablesorCoercedVariablesin crucial classes related to Execution and classes required for the upcoming traverser changes in #2773.What didn't change
There are other classes containing a map of variables such as
FieldCollectionParameters. These classes have been left unchanged primarily to keep the scope of this PR manageable. In these unchanged classes, there was far less ambiguity about coercion. I can extendRawVariablesandCoercedVariablesto all other classes in another PR.What is in the next PR
After this PR is merged in I can help finish off #2773 and fix both double coercion examples I outlined above.