Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 0 additions & 1 deletion src/main/java/graphql/validation/ValidationErrorType.java
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,6 @@ public enum ValidationErrorType implements ValidationErrorClassification {
NullValueForNonNullArgument,
SubscriptionMultipleRootFields,
SubscriptionIntrospectionRootField,
ForbidSkipAndIncludeOnSubscriptionRoot,
UniqueObjectFieldName,
UnknownOperation
}
4 changes: 2 additions & 2 deletions src/main/java/graphql/validation/Validator.java
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@
import graphql.validation.rules.PossibleFragmentSpreads;
import graphql.validation.rules.ProvidedNonNullArguments;
import graphql.validation.rules.ScalarLeaves;
import graphql.validation.rules.SubscriptionRootField;
import graphql.validation.rules.SubscriptionUniqueRootField;
import graphql.validation.rules.UniqueArgumentNames;
import graphql.validation.rules.UniqueDirectiveNamesPerLocation;
import graphql.validation.rules.UniqueFragmentNames;
Expand Down Expand Up @@ -155,7 +155,7 @@ public List<AbstractRule> createRules(ValidationContext validationContext, Valid
UniqueVariableNames uniqueVariableNamesRule = new UniqueVariableNames(validationContext, validationErrorCollector);
rules.add(uniqueVariableNamesRule);

SubscriptionRootField uniqueSubscriptionRootField = new SubscriptionRootField(validationContext, validationErrorCollector);
SubscriptionUniqueRootField uniqueSubscriptionRootField = new SubscriptionUniqueRootField(validationContext, validationErrorCollector);
rules.add(uniqueSubscriptionRootField);

UniqueObjectFieldName uniqueObjectFieldName = new UniqueObjectFieldName(validationContext, validationErrorCollector);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,38 +6,30 @@
import graphql.execution.FieldCollectorParameters;
import graphql.execution.MergedField;
import graphql.execution.MergedSelectionSet;
import graphql.execution.RawVariables;
import graphql.execution.ValuesResolver;
import graphql.language.Directive;
import graphql.language.NodeUtil;
import graphql.language.OperationDefinition;
import graphql.language.VariableDefinition;
import graphql.language.Selection;
import graphql.schema.GraphQLObjectType;
import graphql.validation.AbstractRule;
import graphql.validation.ValidationContext;
import graphql.validation.ValidationErrorCollector;

import java.util.List;

import static graphql.Directives.INCLUDE_DIRECTIVE_DEFINITION;
import static graphql.Directives.SKIP_DIRECTIVE_DEFINITION;
import static graphql.language.OperationDefinition.Operation.SUBSCRIPTION;
import static graphql.validation.ValidationErrorType.SubscriptionIntrospectionRootField;
import static graphql.validation.ValidationErrorType.SubscriptionMultipleRootFields;
import static graphql.validation.ValidationErrorType.ForbidSkipAndIncludeOnSubscriptionRoot;


/**
* A subscription operation must only have one root field
* A subscription operation's single root field must not be an introspection field
* https://spec.graphql.org/draft/#sec-Single-root-field
*
* A subscription operation's root field must not have neither @skip nor @include directives
*/
@Internal
public class SubscriptionRootField extends AbstractRule {
public class SubscriptionUniqueRootField extends AbstractRule {
private final FieldCollector fieldCollector = new FieldCollector();
public SubscriptionRootField(ValidationContext validationContext, ValidationErrorCollector validationErrorCollector) {
public SubscriptionUniqueRootField(ValidationContext validationContext, ValidationErrorCollector validationErrorCollector) {
super(validationContext, validationErrorCollector);
}

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

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

// This coercion takes into account default values for variables
List<VariableDefinition> variableDefinitions = operationDef.getVariableDefinitions();
CoercedVariables coercedVariableValues = ValuesResolver.coerceVariableValues(
getValidationContext().getSchema(),
variableDefinitions,
RawVariables.emptyVariables(),
getValidationContext().getGraphQLContext(),
getValidationContext().getI18n().getLocale());

FieldCollectorParameters collectorParameters = FieldCollectorParameters.newParameters()
.schema(getValidationContext().getSchema())
.fragments(NodeUtil.getFragmentsByName(getValidationContext().getDocument()))
.variables(coercedVariableValues.toMap())
.variables(CoercedVariables.emptyVariables().toMap())
.objectType(subscriptionType)
.graphQLContext(getValidationContext().getGraphQLContext())
.build();

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

if (fields.size() > 1) {
String message = i18n(SubscriptionMultipleRootFields, "SubscriptionUniqueRootField.multipleRootFields", operationDef.getName());
Expand All @@ -73,30 +57,16 @@ public void checkOperationDefinition(OperationDefinition operationDef) {

MergedField mergedField = fields.getSubFieldsList().get(0);


if (isIntrospectionField(mergedField)) {
String message = i18n(SubscriptionIntrospectionRootField, "SubscriptionIntrospectionRootField.introspectionRootField", operationDef.getName(), mergedField.getName());
addError(SubscriptionIntrospectionRootField, mergedField.getSingleField().getSourceLocation(), message);
}

if (hasSkipOrIncludeDirectives(mergedField)) {
String message = i18n(ForbidSkipAndIncludeOnSubscriptionRoot, "SubscriptionRootField.forbidSkipAndIncludeOnSubscriptionRoot", operationDef.getName(), mergedField.getName());
addError(ForbidSkipAndIncludeOnSubscriptionRoot, mergedField.getSingleField().getSourceLocation(), message);
}
}
}
}

private boolean isIntrospectionField(MergedField field) {
return field.getName().startsWith("__");
}

private boolean hasSkipOrIncludeDirectives(MergedField field) {
List<Directive> directives = field.getSingleField().getDirectives();
for (Directive directive : directives) {
if (directive.getName().equals(SKIP_DIRECTIVE_DEFINITION.getName()) || directive.getName().equals(INCLUDE_DIRECTIVE_DEFINITION.getName())) {
return true;
}
}
return false;
}
}
3 changes: 2 additions & 1 deletion src/main/resources/i18n/Validation.properties
Original file line number Diff line number Diff line change
Expand Up @@ -68,8 +68,9 @@ 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
SubscriptionIntrospectionRootField.introspectionRootField=Validation error ({0}) : Subscription operation ''{1}'' root field ''{2}'' cannot be an introspection field
SubscriptionRootField.forbidSkipAndIncludeOnSubscriptionRoot=Validation error ({0}) : Subscription operation ''{1}'' root field ''{2}'' must not use @skip nor @include directives in top level selection
SubscriptionIntrospectionRootField.introspectionRootFieldWithFragment=Validation error ({0}) : Subscription operation ''{1}'' fragment root field ''{2}'' cannot be an introspection field
#
UniqueArgumentNames.uniqueArgument=Validation error ({0}) : There can be only one argument named ''{1}''
#
Expand Down
4 changes: 2 additions & 2 deletions src/main/resources/i18n/Validation_de.properties
Original file line number Diff line number Diff line change
Expand Up @@ -60,9 +60,9 @@ ScalarLeaves.subselectionOnLeaf=Validierungsfehler ({0}) : Unterauswahl für Bla
ScalarLeaves.subselectionRequired=Validierungsfehler ({0}) : Unterauswahl erforderlich für Typ ''{1}'' des Feldes ''{2}''
#
SubscriptionUniqueRootField.multipleRootFields=Validierungsfehler ({0}) : Subscription operation ''{1}'' muss genau ein root field haben
SubscriptionUniqueRootField.multipleRootFieldsWithFragment=Validierungsfehler ({0}) : Subscription operation ''{1}'' muss genau ein root field mit Fragmenten haben
SubscriptionIntrospectionRootField.introspectionRootField=Validierungsfehler ({0}) : Subscription operation ''{1}'' 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
#
SubscriptionIntrospectionRootField.introspectionRootFieldWithFragment=Validierungsfehler ({0}) : Subscription operation ''{1}'' fragment root field ''{2}'' kann kein introspection field sein
#
UniqueArgumentNames.uniqueArgument=Validierungsfehler ({0}) : Es kann nur ein Argument namens ''{1}'' geben
#
Expand Down
3 changes: 2 additions & 1 deletion src/main/resources/i18n/Validation_nl.properties
Original file line number Diff line number Diff line change
Expand Up @@ -58,8 +58,9 @@ ScalarLeaves.subselectionOnLeaf=Validatiefout ({0}) : Sub-selectie niet toegesta
ScalarLeaves.subselectionRequired=Validatiefout ({0}) : Sub-selectie verplicht voor type ''{1}'' van veld ''{2}''
#
SubscriptionUniqueRootField.multipleRootFields=Validatiefout ({0}) : Subscription operation ''{1}'' moet exact één root field hebben
SubscriptionUniqueRootField.multipleRootFieldsWithFragment=Validatiefout ({0}) : Subscription operation ''{1}'' moet exact één root field met fragmenten hebben
SubscriptionIntrospectionRootField.introspectionRootField=Validatiefout ({0}) : Subscription operation ''{1}'' 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
SubscriptionIntrospectionRootField.introspectionRootFieldWithFragment=Validatiefout ({0}) : Subscription operation ''{1}'' fragment root field ''{2}'' kan geen introspectieveld zijn
#
UniqueArgumentNames.uniqueArgument=Validatiefout ({0}) : Er mag maar één argument met naam ''{1}'' bestaan
#
Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import graphql.validation.ValidationErrorType
import graphql.validation.Validator
import spock.lang.Specification

class SubscriptionRootFieldTest extends Specification {
class SubscriptionUniqueRootFieldTest extends Specification {
def "5.2.3.1 subscription with only one root field passes validation"() {
given:
def subscriptionOneRoot = '''
Expand Down Expand Up @@ -286,7 +286,6 @@ class SubscriptionRootFieldTest extends Specification {
then:
validationErrors.empty
}

static List<ValidationError> validate(String query) {
def document = new Parser().parseDocument(query)
return new Validator().validateDocument(SpecValidationSchema.specValidationSchema, document, Locale.ENGLISH)
Expand Down
Loading