-
Notifications
You must be signed in to change notification settings - Fork 1.2k
Allowing document compiler to handles directives and values down in the field tree #3757
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
Conversation
| when: | ||
| def result = localCompileToDocument(schema, QUERY, null, fields, noVariables) | ||
| def printed = AstPrinter.printAst(new AstSorter().sort(result.document)) | ||
| def printed = printDoc(result.document) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
just to clean up the tests a little
|
|
||
|
|
||
| def "test a combination of plain objects and interfaces will be all variables"() { | ||
| def "test a combination of plain objects and interfaces with all variables and no variables"() { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I added two test states here - with all variables and with no variables
| v3: [arg1: "barFragArg"], | ||
| v4: [arg1: "barArg"], | ||
| v5: [arg1: "fooArg"]] | ||
|
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is the extra test state. Using no variables
| listField1(arg: [1, 2, 3]) | ||
| listField2(arg: [{arg1 : "v1", arg2 : [{arg1 : "v1.1"}]}, {arg1 : "v2"}, {arg1 : "v3"}]) | ||
| } | ||
| ''' |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This works - all the values are inlined as expected
| } | ||
|
|
||
| private ExecutableNormalizedOperation createNormalizedTree(GraphQLSchema schema, String query, Map<String, Object> variables = [:]) { | ||
| def "can extract variables or inline values for directives on the query"() { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Added this test for directives - it does NOT work - directives are ignored and removed
Test Results 311 files 311 suites 47s ⏱️ Results for commit 053aac4. ♻️ This comment has been updated with latest results. |
| bar(barArg: "barArgVar") @optIn(to: ["optToX"]) | ||
| } | ||
| } | ||
| ''' |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This currently produces
query named {
foo(fooArg: "fooArgVar") {
bar(barArg: "barArgVar")
}
}
Which is wrong!!
| bar(barArg: $v0) | ||
| } | ||
| } | ||
| ''' |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The above is also wrong but I made the assertion pass for now - so I can run both tests. But I know this to be wrong
…o print them back out - we need NormalisedValues for this
| Directive directive = directives.get(i); | ||
| GraphQLDirective graphQLDirective = resolvedDirectives.get(i); | ||
| directiveCounterParts.put(graphQLDirective, directive); | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Question - this is mapping the AST directive to its GraphQLDirective counter part by index - does directivesResolver return the list in the same order and size.
| * argument value making. | ||
| */ | ||
| @Internal | ||
| class ArgumentMaker { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
as the comment says - breaking out code into its own class
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I like the class name, reminds me of this Monty Python sketch https://www.youtube.com/watch?v=uLlv_aZjHXc
I'd like to have an argument please
| String argName, | ||
| NormalizedInputValue normalizedInputValue) { | ||
| return false; | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
since this is a SPI - we cant require them to implement this. Or we could make it a breaking change say
| normalizedVariableValues = ValuesResolver.getNormalizedVariableValues(graphQLSchema, | ||
| variableDefinitions, | ||
| inputVariables, | ||
| executionInput.getGraphQLContext(), executionInput.getLocale()); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is new - we will coerce variables into rutime form AND Normalised literal form so that the ExecutionContext can have them AND the QueryDirectives can have them in the DFE
| public NormalizedVariables getNormalizedVariables() { | ||
| return normalizedVariables; | ||
| } | ||
|
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
just passing on NormalizedVariables
| QueryDirectives queryDirectives = new QueryDirectivesImpl(field, | ||
| executionContext.getGraphQLSchema(), | ||
| executionContext.getCoercedVariables().toMap(), | ||
| executionContext.getNormalizedVariables().toMap(), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
needed inside to make normalized values where they refeference operation variables
| public String toString() { | ||
| return normalisedVariables.toString(); | ||
| } | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We have Raw, Coerced and NormalizedVariables now as distinct classes
|
|
||
| return result; | ||
|
|
||
| return NormalizedVariables.of(result); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
distinct class now used
| } | ||
|
|
||
| public Map<String, List<GraphQLDirective>> resolveDirectives(List<Directive> directives, GraphQLSchema schema, Map<String, Object> variables, GraphQLContext graphQLContext, Locale locale) { | ||
| public BiMap<GraphQLDirective, Directive> resolveDirectives(List<Directive> directives, GraphQLSchema schema, Map<String, Object> variables, GraphQLContext graphQLContext, Locale locale) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The only use of this internal class is QueryDirectivesImpl - so we can change this with impunity
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Map<String, List> was a pointless modelling - BiMap<GraphQLDirective, Directive is what we need
| * | ||
| * @return a map of applied directive to named argument values | ||
| */ | ||
| Map<QueryAppliedDirective, Map<String, NormalizedInputValue>> getNormalizedInputValueByImmediateAppliedDirectives(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
for a given QueryAppliedDirective - we can get a normalised view of its argument values
| private static SelectionSet selectionSet(List<Field> fields) { | ||
| return newSelectionSet().selections(fields).build(); | ||
| } | ||
|
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The code below got moved into ArgumentMaker
| .graphQLContext({ it.put(InputInterceptor.class, interceptor) }) | ||
| .variables( | ||
| "booleanArg": "truthy", | ||
| "booleanArg": false, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This was never a valid boolean value
| @Override | ||
| StringValue valueToLiteral(@NotNull Object input, @NotNull GraphQLContext graphQLContext, @NotNull Locale locale) { | ||
| return new StringValue(input.toString()) | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is now needed because during "execution" we create normalised values.
This is something contentious. If a SCALAR did not previously have this, they require it now to work.
So we want this. We need to discuss but I think so otherwise I would not have put it in place
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That's very interesting! I want to chat about this too
| import static graphql.normalized.ExecutableNormalizedOperationToAstCompiler.compileToDocumentWithDeferSupport | ||
|
|
||
| abstract class ExecutableNormalizedOperationToAstCompilerTest extends Specification { | ||
| abstract class ENOToAstCompilerTestBase extends Specification { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
made a common base - this class is getting WAY to big
| static printDoc(Document doc) { | ||
| return AstPrinter.printAst(sortDoc(doc)) | ||
| } | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
most of the above is moving common test helper code around
| name @optIn(to: "devOps") | ||
| foo1(arg: {arg1 : "Subscription"}) @optIn(to: ["foo"]) { | ||
| name @optIn(to: ["devOps"]) | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
was NOT correct before
| executionInput.getGraphQLContext(), | ||
| executionInput.getLocale()); | ||
|
|
||
| normalizedVariableValues = FpKit.intraThreadMemoize(() -> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I made this a supplier - if not one asks for normalised values of directives - then no work will be done.
QueryDirectivesIpl is also lazy by design -so this wont be asked for until some one asks for QueryDirectives
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nice
| return variablePredicate != null && variablePredicate.shouldMakeVariable(executableNormalizedField, queryAppliedDirective, argName, normalizedInputValue); | ||
| } else { | ||
| return variablePredicate != null && variablePredicate.shouldMakeVariable(executableNormalizedField, argName, normalizedInputValue); | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this internal call back is the same - when queryAppliedDirective is null - it must be just for a field
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This could be added as a comment in the code, that the else branch is only for a field
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
done
| } | ||
| } | ||
| } | ||
| ''' |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
notice all the fragment directives
fragment InnerDetails on Book @timeout(afterMillis: 27)
well they dont get picked up as "immediate" query directives. This is NOT new behavior
Only immediate field directives are captured
| * Test related to ENO and directives | ||
| */ | ||
| class ENOToAstCompilerDirectivesTest extends ENOToAstCompilerTestBase { | ||
| class ExecutableNormalizedOperationToAstCompilerDirectivesTest extends ENOToAstCompilerTestBase { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
name line up better in IDEA - even though its TOO long a class name
| vars == [v0: "barArgVar", v1: ["optToX"], v2: ["optToY"], v3: "fooArgVar", v4: false] | ||
| // | ||
| // the below is what ir currently produces but its WRONG | ||
| // fix up when the other tests starts to work |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is this test working as expected now?
…ument-compiler # Conflicts: # src/main/java/graphql/execution/Execution.java
…ument-compiler # Conflicts: # src/main/java/graphql/execution/Execution.java # src/main/java/graphql/normalized/ExecutableNormalizedOperationToAstCompiler.java
Previously the ENO compiler would not respect "immediate" directives on a field.
While it has the
QueryDirectivesobject - this doe not have "Normalised" values for directive arguments.This PR adds that. This PR is more extensive that I had hoped - but adding Normalised values in multiple places will set us up for betterness later