Skip to content

Conversation

dondonz
Copy link
Member

@dondonz dondonz commented Mar 29, 2025

Fixes #3868 and implements graphql/graphql-spec#860

We had an existing validation for subscription root fields. This adds on a new requirement that subscription root fields can't contain the @include nor @skip directives. See the spec link for more.

This could be considered a "breaking change" but it is more correctly a new validation requirement.

@@ -27,7 +27,7 @@
import graphql.validation.rules.PossibleFragmentSpreads;
import graphql.validation.rules.ProvidedNonNullArguments;
import graphql.validation.rules.ScalarLeaves;
import graphql.validation.rules.SubscriptionUniqueRootField;
import graphql.validation.rules.SubscriptionRootField;
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I changed this rule name to be more generic, reflecting that it does 2 checks on the root field

@@ -39,16 +47,24 @@ public void checkOperationDefinition(OperationDefinition operationDef) {

GraphQLObjectType subscriptionType = getValidationContext().getSchema().getSubscriptionType();

// This coercion takes into account default values for variables
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is fixing an edge case - this check previously didn't take into account default variable values because there was previously no coercion.

That caused my tests to fail (see later) because I had set a default boolean value for include and skip.

.objectType(subscriptionType)
.graphQLContext(getValidationContext().getGraphQLContext())
.build();

MergedSelectionSet fields = fieldCollector.collectFields(collectorParameters, operationDef.getSelectionSet());
List<Selection> subscriptionSelections = operationDef.getSelectionSet().getSelections();
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Unused variable


private boolean hasSkipOrIncludeDirectives(MergedField field) {
List<Directive> directives = field.getSingleField().getDirectives();
for (Directive directive : directives) {
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Given recent performance improvements I am using plain for loops

@@ -68,9 +68,8 @@ ScalarLeaves.subselectionOnLeaf=Validation error ({0}) : Subselection not allowe
ScalarLeaves.subselectionRequired=Validation error ({0}) : Subselection required for type ''{1}'' of field ''{2}''
#
SubscriptionUniqueRootField.multipleRootFields=Validation error ({0}) : Subscription operation ''{1}'' must have exactly one root field
SubscriptionUniqueRootField.multipleRootFieldsWithFragment=Validation error ({0}) : Subscription operation ''{1}'' must have exactly one root field with fragments
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Unused messages

Copy link
Contributor

github-actions bot commented Mar 29, 2025

Test Results

  312 files    312 suites   45s ⏱️
3 583 tests 3 577 ✅ 6 💤 0 ❌
3 672 runs  3 666 ✅ 6 💤 0 ❌

Results for commit 0219c47.

♻️ This comment has been updated with latest results.

SubscriptionIntrospectionRootField.introspectionRootField=Validierungsfehler ({0}) : Subscription operation ''{1}'' root field ''{2}'' kann kein introspection field sein
SubscriptionIntrospectionRootField.introspectionRootFieldWithFragment=Validierungsfehler ({0}) : Subscription operation ''{1}'' fragment root field ''{2}'' kann kein introspection field sein
SubscriptionRootField.forbidSkipAndIncludeOnSubscriptionRoot=Validierungsfehler ({0}) : Subscription operation ''{1}'' root field ''{2}'' darf weder @skip noch @include directive in top level selection
#
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Check my German please

def "valid subscription with @skip and @include directives on subfields"() {
given:
def query = """
subscription MySubscription(\$bool: Boolean = true) {
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is what I mean by default values. When an empty coerced variables map is passed, this test fails

SubscriptionIntrospectionRootField.introspectionRootField=Validatiefout ({0}) : Subscription operation ''{1}'' root field ''{2}'' kan geen introspectieveld zijn
SubscriptionIntrospectionRootField.introspectionRootFieldWithFragment=Validatiefout ({0}) : Subscription operation ''{1}'' fragment root field ''{2}'' kan geen introspectieveld zijn
SubscriptionRootField.forbidSkipAndIncludeOnSubscriptionRoot=Validation error ({0}) : Subscription operation ''{1}'' root field ''{2}'' must not use @skip nor @include directives in top level selection
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Add English placeholder

@dondonz dondonz added the spec-change Tracking GraphQL specification changes label Mar 29, 2025
@dondonz dondonz added the breaking change requires a new major version to be relased label Apr 1, 2025
@dondonz dondonz merged commit 12273f4 into master Apr 1, 2025
2 checks passed
@dondonz dondonz deleted the subscriptions-root-field branch April 1, 2025 20:43
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
breaking change requires a new major version to be relased spec-change Tracking GraphQL specification changes
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Tracking spec change: Prevent @skip and @include on root subscription selection set
2 participants