This adds support for QueryAppliedDirective on operations and documents#4297
This adds support for QueryAppliedDirective on operations and documents#4297
Conversation
Test ReportTest Results
Code Coverage (Java 25)
Changed Class Coverage (7 classes)
ExecutableNormalizedOperationFactory — method details
|
| return directives.stream().map(this::toAppliedDirective).collect(ImmutableList.toImmutableList()); | ||
| } | ||
|
|
||
| public QueryAppliedDirective toAppliedDirective(GraphQLDirective directive) { |
There was a problem hiding this comment.
These methods used to be in QueryDirectivesImpl but I moved them here to be common and used now in two places
|
|
||
| @Internal | ||
| @NullMarked | ||
| public class OperationDirectivesResolver { |
There was a problem hiding this comment.
The new extracting class is Internal - exposed later via ExecutionContext and ENO
…ts - use Immutable signature
…ts - used field to make it cheaper
| // preconditions | ||
| assertNotNull(executionId, "You must provide a query identifier"); | ||
| return new ExecutionContext(this); | ||
| } |
There was a problem hiding this comment.
moved build down to the bottom for code OCD reasons
| commonIntegrationAsserts(resolveDirectives) | ||
|
|
||
| when: | ||
| def normalizedOperation = executionContext.getNormalizedQueryTree().get() |
There was a problem hiding this comment.
here the ENO has the same values
| .inputValueWithState(argument.getArgumentValue()) | ||
| .build(); | ||
| } | ||
|
|
There was a problem hiding this comment.
moved methods so they can be re-used
…ts - made it a supplier for performance
| .responseMapFactory().getOr(ResponseMapFactory.DEFAULT); | ||
|
|
||
| Supplier<Map<OperationDefinition, ImmutableList<QueryAppliedDirective>>> operationDirectives = FpKit.interThreadMemoize(() -> | ||
| operationDirectivesResolver.resolveDirectives(document, graphQLSchema, coercedVariables, graphQLContext, locale)); |
There was a problem hiding this comment.
This is a memoizing supplier for performance reasons. if you never ask for the directives - no work is done - only the allocation of a interThreadMemoize object
| this.executionInput = builder.executionInput; | ||
| this.dataLoaderDispatcherStrategy = builder.dataLoaderDispatcherStrategy; | ||
| this.queryTree = FpKit.interThreadMemoize(() -> ExecutableNormalizedOperationFactory.createExecutableNormalizedOperation(graphQLSchema, operationDefinition, fragmentsByName, coercedVariables)); | ||
| this.propagateErrorsOnNonNullContractFailure = builder.propagateErrorsOnNonNullContractFailure; |
There was a problem hiding this comment.
rejigged this code to a maker method
|
|
||
| private Supplier<ExecutableNormalizedOperation> mkExecutableNormalizedOperation() { | ||
| return FpKit.interThreadMemoize(() -> { | ||
| Options options = Options.defaultOptions().graphQLContext(graphQLContext).locale(locale); |
There was a problem hiding this comment.
fixed a bug here - graphql context and locale was never respected before
| assertNotNull(executionId, "You must provide a query identifier"); | ||
| return new ExecutionContext(this); | ||
| } | ||
|
|
…ts -added more tests
| import java.util.function.Consumer; | ||
| import java.util.function.Supplier; | ||
|
|
||
| import static graphql.normalized.ExecutableNormalizedOperationFactory.*; |
…' into extract-out-operation-directives
Coverage Drop AnalysisThe "Per-Class Coverage Gate" CI check is failing due to two classes showing coverage regressions. Here's a deep analysis of each: 1.
|
| Metric | Before | Removed (all covered) | After | Reported |
|---|---|---|---|---|
| Line | 82 / 84 = 97.6% | 10 covered lines | (82−10) / (84−10) = 72/74 = 97.3% | 97.3% ✓ |
| Branch | 8 / 10 = 80.0% | 2 covered branches | (8−2) / (10−2) = 6/8 = 75.0% | 75.0% ✓ |
| Method | 17 / 18 = 94.4% | 2 covered methods | (17−2) / (18−2) = 15/16 = 93.75% | 93.8% ✓ |
Why does this happen? If a class has coverage C/T and you remove R items that were all covered:
New coverage = (C − R) / (T − R)
This is less than C/T whenever R/R > C/T, i.e. whenever 100% > C/T — which is true for any class not already at 100%. Removing perfectly-covered code from a not-perfectly-covered class always lowers the percentage, even though nothing became less tested. The 2 missed lines/branches now represent a larger fraction of a smaller total (2/74 instead of 2/84, 2/8 instead of 2/10).
This is a mathematical artifact of the ratio, not a real regression. The extracted code is now tested via DirectivesResolver and the new OperationDirectivesResolverTest.
2. ExecutableNormalizedOperationFactory (outer class) — Line: 84.0% (was 92.0%), Method: 75.0% (was 87.5%)
Root cause: The 4-arg convenience overload of createExecutableNormalizedOperation(schema, operationDefinition, fragments, coercedVars) (line 311-320) lost its only caller.
Previously, ExecutionContext constructed the normalized query tree by calling this 4-arg overload:
ExecutableNormalizedOperationFactory.createExecutableNormalizedOperation(
graphQLSchema, operationDefinition, fragmentsByName, coercedVariables);This PR changes ExecutionContext.mkExecutableNormalizedOperation() to call the 5-arg overload instead, passing Options with graphQLContext and locale:
Options options = Options.defaultOptions().graphQLContext(graphQLContext).locale(locale);
return createExecutableNormalizedOperation(
graphQLSchema, operationDefinition, fragmentsByName, coercedVariables, options);Integration tests that execute full queries go through ExecutionContext, which was the only path exercising the 4-arg (schema, OperationDefinition, fragments, coercedVars) overload. Direct unit tests in ExecutableNormalizedOperationFactoryTest use the (schema, Document, operationName, coercedVars, options) overload instead. This matches the numbers exactly:
Baseline: 23 covered, 2 missed lines (92.0%) / 7 covered, 1 missed method (87.5%)
Line: (23−2) / (25+0) = 21/25 = 84.0% ← 2 lines in the 4-arg method body lost coverage
Method: (7−1) / (8+0) = 6/8 = 75.0% ← the 4-arg method itself is now uncovered
This is a real but trivial gap — the uncovered method is a one-line delegator to the 5-arg version. It could be fixed by adding a test that calls the 4-arg overload, or the baseline can be updated since the method is a trivial convenience wrapper.
Summary
| Class | Verdict | Action needed |
|---|---|---|
QueryDirectivesImpl |
False positive (refactor shifted ratios) | Update baseline |
ExecutableNormalizedOperationFactory |
Real but trivial (convenience overload lost its only caller) | Update baseline or add a trivial test |
See #4295