Abstract base class for validation rules

This commit is contained in:
Vladimir Razuvaev 2017-08-18 20:48:27 +07:00
parent 9499e5ae8e
commit 203fddfe4e
37 changed files with 268 additions and 116 deletions

View File

@ -43,6 +43,49 @@ But note that this is deprecated format and will be removed in future versions.
In general, if new default formatting doesn't work for you - just set [your own error
formatter](http://webonyx.github.io/graphql-php/error-handling/#custom-error-handling-and-formatting).
### Breaking: Validation rules now have abstract base class
Previously any callable was accepted by DocumentValidator as validation rule. Now only instances of
`GraphQL\Validator\Rules\AbstractValidationRule` are allowed.
If you were using custom validation rules, just wrap them with
`GraphQL\Validator\Rules\CustomValidationRule` (created for backwards compatibility).
Before:
```php
use GraphQL\Validator\DocumentValidator;
$myRule = function(ValidationContext $context) {};
DocumentValidator::validate($schema, $ast, [$myRule]);
```
After:
```php
use GraphQL\Validator\Rules\CustomValidationRule;
use GraphQL\Validator\DocumentValidator;
$myRule = new CustomValidationRule('MyRule', function(ValidationContext $context) {});
DocumentValidator::validate($schema, $ast, [$myRule]);
```
Also `DocumentValidator::addRule()` signature changed.
Before the change:
```php
use GraphQL\Validator\DocumentValidator;
$myRule = function(ValidationContext $context) {};
DocumentValidator::addRule('MyRuleName', $myRule);
```
After the change:
```php
use GraphQL\Validator\DocumentValidator;
$myRule = new CustomValidationRulefunction('MyRule', ValidationContext $context) {});
DocumentValidator::addRule($myRule);
```
### Breaking: AST now uses `NodeList` vs array for lists of nodes
It helps us unserialize AST from array lazily. This change affects you only if you use `array_`
functions with AST or mutate AST directly.

View File

@ -113,9 +113,18 @@ class GraphQL
$documentNode = Parser::parse(new Source($source ?: '', 'GraphQL'));
}
/** @var QueryComplexity $queryComplexity */
$queryComplexity = DocumentValidator::getRule('QueryComplexity');
$queryComplexity->setRawVariableValues($variableValues);
// FIXME
if (!empty($validationRules)) {
foreach ($validationRules as $rule) {
if ($rule instanceof QueryComplexity) {
$rule->setRawVariableValues($variableValues);
}
}
} else {
/** @var QueryComplexity $queryComplexity */
$queryComplexity = DocumentValidator::getRule(QueryComplexity::class);
$queryComplexity->setRawVariableValues($variableValues);
}
$validationErrors = DocumentValidator::validate($schema, $documentNode, $validationRules);
@ -223,7 +232,7 @@ class GraphQL
*
* @return Directive[]
*/
public static function getInternalDirectives()
public static function getStandardDirectives()
{
return array_values(Directive::getInternalDirectives());
}
@ -233,9 +242,17 @@ class GraphQL
*
* @return Type[]
*/
public static function getInternalTypes()
public static function getStandardTypes()
{
return Type::getInternalTypes();
return array_values(Type::getInternalTypes());
}
/**
* @return array
*/
public static function getStandardValidationRules()
{
return array_values(DocumentValidator::defaultRules());
}
/**
@ -253,4 +270,15 @@ class GraphQL
{
Executor::setPromiseAdapter($promiseAdapter);
}
/**
* Returns directives defined in GraphQL spec
*
* @deprecated Renamed to getStandardDirectives
* @return Directive[]
*/
public static function getInternalDirectives()
{
return self::getStandardDirectives();
}
}

View File

@ -18,6 +18,7 @@ use GraphQL\Type\Definition\NonNull;
use GraphQL\Type\Definition\Type;
use GraphQL\Utils\Utils;
use GraphQL\Utils\TypeInfo;
use GraphQL\Validator\Rules\AbstractValidationRule;
use GraphQL\Validator\Rules\ArgumentsOfCorrectType;
use GraphQL\Validator\Rules\DefaultValuesOfCorrectType;
use GraphQL\Validator\Rules\DisableIntrospection;
@ -53,6 +54,8 @@ class DocumentValidator
private static $defaultRules;
private static $securityRules;
private static $initRules = false;
/**
@ -63,8 +66,8 @@ class DocumentValidator
public static function allRules()
{
if (!self::$initRules) {
self::$rules = array_merge(static::defaultRules(), self::$rules);
self::$initRules = true;
static::$rules = array_merge(static::defaultRules(), self::securityRules(), self::$rules);
static::$initRules = true;
}
return self::$rules;
@ -74,64 +77,82 @@ class DocumentValidator
{
if (null === self::$defaultRules) {
self::$defaultRules = [
'UniqueOperationNames' => new UniqueOperationNames(),
'LoneAnonymousOperation' => new LoneAnonymousOperation(),
'KnownTypeNames' => new KnownTypeNames(),
'FragmentsOnCompositeTypes' => new FragmentsOnCompositeTypes(),
'VariablesAreInputTypes' => new VariablesAreInputTypes(),
'ScalarLeafs' => new ScalarLeafs(),
'FieldsOnCorrectType' => new FieldsOnCorrectType(),
'UniqueFragmentNames' => new UniqueFragmentNames(),
'KnownFragmentNames' => new KnownFragmentNames(),
'NoUnusedFragments' => new NoUnusedFragments(),
'PossibleFragmentSpreads' => new PossibleFragmentSpreads(),
'NoFragmentCycles' => new NoFragmentCycles(),
'UniqueVariableNames' => new UniqueVariableNames(),
'NoUndefinedVariables' => new NoUndefinedVariables(),
'NoUnusedVariables' => new NoUnusedVariables(),
'KnownDirectives' => new KnownDirectives(),
'UniqueDirectivesPerLocation' => new UniqueDirectivesPerLocation(),
'KnownArgumentNames' => new KnownArgumentNames(),
'UniqueArgumentNames' => new UniqueArgumentNames(),
'ArgumentsOfCorrectType' => new ArgumentsOfCorrectType(),
'ProvidedNonNullArguments' => new ProvidedNonNullArguments(),
'DefaultValuesOfCorrectType' => new DefaultValuesOfCorrectType(),
'VariablesInAllowedPosition' => new VariablesInAllowedPosition(),
'OverlappingFieldsCanBeMerged' => new OverlappingFieldsCanBeMerged(),
'UniqueInputFieldNames' => new UniqueInputFieldNames(),
// Query Security
'DisableIntrospection' => new DisableIntrospection(DisableIntrospection::DISABLED), // DEFAULT DISABLED
'QueryDepth' => new QueryDepth(QueryDepth::DISABLED), // default disabled
'QueryComplexity' => new QueryComplexity(QueryComplexity::DISABLED), // default disabled
UniqueOperationNames::class => new UniqueOperationNames(),
LoneAnonymousOperation::class => new LoneAnonymousOperation(),
KnownTypeNames::class => new KnownTypeNames(),
FragmentsOnCompositeTypes::class => new FragmentsOnCompositeTypes(),
VariablesAreInputTypes::class => new VariablesAreInputTypes(),
ScalarLeafs::class => new ScalarLeafs(),
FieldsOnCorrectType::class => new FieldsOnCorrectType(),
UniqueFragmentNames::class => new UniqueFragmentNames(),
KnownFragmentNames::class => new KnownFragmentNames(),
NoUnusedFragments::class => new NoUnusedFragments(),
PossibleFragmentSpreads::class => new PossibleFragmentSpreads(),
NoFragmentCycles::class => new NoFragmentCycles(),
UniqueVariableNames::class => new UniqueVariableNames(),
NoUndefinedVariables::class => new NoUndefinedVariables(),
NoUnusedVariables::class => new NoUnusedVariables(),
KnownDirectives::class => new KnownDirectives(),
UniqueDirectivesPerLocation::class => new UniqueDirectivesPerLocation(),
KnownArgumentNames::class => new KnownArgumentNames(),
UniqueArgumentNames::class => new UniqueArgumentNames(),
ArgumentsOfCorrectType::class => new ArgumentsOfCorrectType(),
ProvidedNonNullArguments::class => new ProvidedNonNullArguments(),
DefaultValuesOfCorrectType::class => new DefaultValuesOfCorrectType(),
VariablesInAllowedPosition::class => new VariablesInAllowedPosition(),
OverlappingFieldsCanBeMerged::class => new OverlappingFieldsCanBeMerged(),
UniqueInputFieldNames::class => new UniqueInputFieldNames(),
];
}
return self::$defaultRules;
}
/**
* @return array
*/
public static function securityRules()
{
// This way of defining rules is deprecated
// When custom security rule is required - it should be just added via DocumentValidator::addRule();
// TODO: deprecate this
if (null === self::$securityRules) {
self::$securityRules = [
DisableIntrospection::class => new DisableIntrospection(DisableIntrospection::DISABLED), // DEFAULT DISABLED
QueryDepth::class => new QueryDepth(QueryDepth::DISABLED), // default disabled
QueryComplexity::class => new QueryComplexity(QueryComplexity::DISABLED), // default disabled
];
}
return self::$securityRules;
}
/**
* Returns validation rule
*
* @param string $name
* @return callable|null
* @return AbstractValidationRule
*/
public static function getRule($name)
{
$rules = static::allRules();
if (isset($rules[$name])) {
return $rules[$name];
}
$name = "GraphQL\\Validator\\Rules\\$name";
return isset($rules[$name]) ? $rules[$name] : null ;
}
/**
* Add rule to list of default validation rules
*
* @param string $name
* @param callable $rule
* @param AbstractValidationRule $rule
*/
public static function addRule($name, callable $rule)
public static function addRule(AbstractValidationRule $rule)
{
self::$rules[$name] = $rule;
self::$rules[$rule->getName()] = $rule;
}
/**
@ -292,7 +313,7 @@ class DocumentValidator
* @param Schema $schema
* @param TypeInfo $typeInfo
* @param DocumentNode $documentNode
* @param array $rules
* @param AbstractValidationRule[] $rules
* @return array
*/
public static function visitUsingRules(Schema $schema, TypeInfo $typeInfo, DocumentNode $documentNode, array $rules)
@ -300,7 +321,7 @@ class DocumentValidator
$context = new ValidationContext($schema, $documentNode, $typeInfo);
$visitors = [];
foreach ($rules as $rule) {
$visitors[] = $rule($context);
$visitors[] = $rule->getVisitor($context);
}
Visitor::visit($documentNode, Visitor::visitWithTypeInfo($typeInfo, Visitor::visitInParallel($visitors)));
return $context->getErrors();

View File

@ -14,7 +14,7 @@ use GraphQL\Type\Introspection;
use GraphQL\Utils\TypeInfo;
use GraphQL\Validator\ValidationContext;
abstract class AbstractQuerySecurity
abstract class AbstractQuerySecurity extends AbstractValidationRule
{
const DISABLED = 0;
@ -83,7 +83,7 @@ abstract class AbstractQuerySecurity
* time we do not know what object type will be used, so we unconditionally
* spread in all fragments.
*
* @see GraphQL\Validator\Rules\OverlappingFieldsCanBeMerged
* @see \GraphQL\Validator\Rules\OverlappingFieldsCanBeMerged
*
* @param ValidationContext $context
* @param Type|null $parentType

View File

@ -0,0 +1,26 @@
<?php
namespace GraphQL\Validator\Rules;
use GraphQL\Error\Error;
use GraphQL\Validator\ValidationContext;
abstract class AbstractValidationRule
{
protected $name;
public function getName()
{
return $this->name ?: get_class($this);
}
public function __invoke(ValidationContext $context)
{
return $this->getVisitor($context);
}
/**
* @param ValidationContext $context
* @return Error[]
*/
abstract public function getVisitor(ValidationContext $context);
}

View File

@ -9,7 +9,7 @@ use GraphQL\Language\Visitor;
use GraphQL\Validator\DocumentValidator;
use GraphQL\Validator\ValidationContext;
class ArgumentsOfCorrectType
class ArgumentsOfCorrectType extends AbstractValidationRule
{
static function badValueMessage($argName, $type, $value, $verboseErrors = [])
{
@ -17,7 +17,7 @@ class ArgumentsOfCorrectType
return "Argument \"$argName\" has invalid value $value.$message";
}
public function __invoke(ValidationContext $context)
public function getVisitor(ValidationContext $context)
{
return [
NodeKind::ARGUMENT => function(ArgumentNode $argNode) use ($context) {

View File

@ -0,0 +1,27 @@
<?php
namespace GraphQL\Validator\Rules;
use GraphQL\Error\Error;
use GraphQL\Validator\ValidationContext;
class CustomValidationRule extends AbstractValidationRule
{
private $visitorFn;
public function __construct($name, callable $visitorFn)
{
$this->name = $name;
$this->visitorFn = $visitorFn;
}
/**
* @param ValidationContext $context
* @return Error[]
*/
public function getVisitor(ValidationContext $context)
{
$fn = $this->visitorFn;
return $fn($context);
}
}

View File

@ -12,7 +12,7 @@ use GraphQL\Type\Definition\NonNull;
use GraphQL\Validator\DocumentValidator;
use GraphQL\Validator\ValidationContext;
class DefaultValuesOfCorrectType
class DefaultValuesOfCorrectType extends AbstractValidationRule
{
static function badValueForDefaultArgMessage($varName, $type, $value, $verboseErrors = null)
{
@ -27,7 +27,7 @@ class DefaultValuesOfCorrectType
"Perhaps you meant to use type $guessType.";
}
public function __invoke(ValidationContext $context)
public function getVisitor(ValidationContext $context)
{
return [
NodeKind::VARIABLE_DEFINITION => function(VariableDefinitionNode $varDefNode) use ($context) {

View File

@ -11,7 +11,7 @@ class DisableIntrospection extends AbstractQuerySecurity
const ENABLED = 1;
private $isEnabled;
public function __construct($enabled)
public function __construct($enabled = self::ENABLED)
{
$this->setEnabled($enabled);
}
@ -31,7 +31,7 @@ class DisableIntrospection extends AbstractQuerySecurity
return $this->isEnabled !== static::DISABLED;
}
public function __invoke(ValidationContext $context)
public function getVisitor(ValidationContext $context)
{
return $this->invokeIfNeeded(
$context,

View File

@ -7,7 +7,7 @@ use GraphQL\Language\AST\NodeKind;
use GraphQL\Utils\Utils;
use GraphQL\Validator\ValidationContext;
class FieldsOnCorrectType
class FieldsOnCorrectType extends AbstractValidationRule
{
static function undefinedFieldMessage($field, $type, array $suggestedTypes = [])
{
@ -29,7 +29,7 @@ class FieldsOnCorrectType
return $message;
}
public function __invoke(ValidationContext $context)
public function getVisitor(ValidationContext $context)
{
return [
NodeKind::FIELD => function(FieldNode $node) use ($context) {

View File

@ -10,7 +10,7 @@ use GraphQL\Type\Definition\Type;
use GraphQL\Utils\TypeInfo;
use GraphQL\Validator\ValidationContext;
class FragmentsOnCompositeTypes
class FragmentsOnCompositeTypes extends AbstractValidationRule
{
static function inlineFragmentOnNonCompositeErrorMessage($type)
{
@ -22,7 +22,7 @@ class FragmentsOnCompositeTypes
return "Fragment \"$fragName\" cannot condition on non composite type \"$type\".";
}
public function __invoke(ValidationContext $context)
public function getVisitor(ValidationContext $context)
{
return [
NodeKind::INLINE_FRAGMENT => function(InlineFragmentNode $node) use ($context) {

View File

@ -7,7 +7,7 @@ use GraphQL\Language\AST\NodeKind;
use GraphQL\Utils\Utils;
use GraphQL\Validator\ValidationContext;
class KnownArgumentNames
class KnownArgumentNames extends AbstractValidationRule
{
public static function unknownArgMessage($argName, $fieldName, $type)
{
@ -19,7 +19,7 @@ class KnownArgumentNames
return "Unknown argument \"$argName\" on directive \"@$directiveName\".";
}
public function __invoke(ValidationContext $context)
public function getVisitor(ValidationContext $context)
{
return [
NodeKind::ARGUMENT => function(ArgumentNode $node, $key, $parent, $path, $ancestors) use ($context) {

View File

@ -9,7 +9,7 @@ use GraphQL\Language\AST\NodeKind;
use GraphQL\Validator\ValidationContext;
use GraphQL\Type\Definition\DirectiveLocation;
class KnownDirectives
class KnownDirectives extends AbstractValidationRule
{
static function unknownDirectiveMessage($directiveName)
{
@ -21,7 +21,7 @@ class KnownDirectives
return "Directive \"$directiveName\" may not be used on \"$location\".";
}
public function __invoke(ValidationContext $context)
public function getVisitor(ValidationContext $context)
{
return [
NodeKind::DIRECTIVE => function (DirectiveNode $node, $key, $parent, $path, $ancestors) use ($context) {

View File

@ -8,14 +8,14 @@ use GraphQL\Language\AST\Node;
use GraphQL\Language\AST\NodeKind;
use GraphQL\Validator\ValidationContext;
class KnownFragmentNames
class KnownFragmentNames extends AbstractValidationRule
{
static function unknownFragmentMessage($fragName)
{
return "Unknown fragment \"$fragName\".";
}
public function __invoke(ValidationContext $context)
public function getVisitor(ValidationContext $context)
{
return [
NodeKind::FRAGMENT_SPREAD => function(FragmentSpreadNode $node) use ($context) {

View File

@ -8,14 +8,14 @@ use GraphQL\Language\AST\NodeKind;
use GraphQL\Language\Visitor;
use GraphQL\Validator\ValidationContext;
class KnownTypeNames
class KnownTypeNames extends AbstractValidationRule
{
static function unknownTypeMessage($type)
{
return "Unknown type \"$type\".";
}
public function __invoke(ValidationContext $context)
public function getVisitor(ValidationContext $context)
{
$skip = function() {return Visitor::skipNode();};

View File

@ -15,14 +15,14 @@ use GraphQL\Validator\ValidationContext;
* A GraphQL document is only valid if when it contains an anonymous operation
* (the query short-hand) that it contains only that one operation definition.
*/
class LoneAnonymousOperation
class LoneAnonymousOperation extends AbstractValidationRule
{
static function anonOperationNotAloneMessage()
{
return 'This anonymous operation must be the only defined operation.';
}
public function __invoke(ValidationContext $context)
public function getVisitor(ValidationContext $context)
{
$operationCount = 0;
return [

View File

@ -1,11 +1,4 @@
<?php
/**
* Created by PhpStorm.
* User: Vladimir
* Date: 11.07.2015
* Time: 18:54
*/
namespace GraphQL\Validator\Rules;
@ -18,7 +11,7 @@ use GraphQL\Language\Visitor;
use GraphQL\Utils\Utils;
use GraphQL\Validator\ValidationContext;
class NoFragmentCycles
class NoFragmentCycles extends AbstractValidationRule
{
static function cycleErrorMessage($fragName, array $spreadNames = [])
{
@ -32,7 +25,7 @@ class NoFragmentCycles
public $spreadPathIndexByName;
public function __invoke(ValidationContext $context)
public function getVisitor(ValidationContext $context)
{
// Tracks already visited fragments to maintain O(N) and to ensure that cycles
// are not redundantly reported.

View File

@ -15,7 +15,7 @@ use GraphQL\Validator\ValidationContext;
*
* @package GraphQL\Validator\Rules
*/
class NoUndefinedVariables
class NoUndefinedVariables extends AbstractValidationRule
{
static function undefinedVarMessage($varName, $opName = null)
{
@ -24,7 +24,7 @@ class NoUndefinedVariables
: "Variable \"$$varName\" is not defined.";
}
public function __invoke(ValidationContext $context)
public function getVisitor(ValidationContext $context)
{
$variableNameDefined = [];

View File

@ -7,7 +7,7 @@ use GraphQL\Language\AST\NodeKind;
use GraphQL\Language\Visitor;
use GraphQL\Validator\ValidationContext;
class NoUnusedFragments
class NoUnusedFragments extends AbstractValidationRule
{
static function unusedFragMessage($fragName)
{
@ -18,7 +18,7 @@ class NoUnusedFragments
public $fragmentDefs;
public function __invoke(ValidationContext $context)
public function getVisitor(ValidationContext $context)
{
$this->operationDefs = [];
$this->fragmentDefs = [];

View File

@ -6,7 +6,7 @@ use GraphQL\Language\AST\NodeKind;
use GraphQL\Language\AST\OperationDefinitionNode;
use GraphQL\Validator\ValidationContext;
class NoUnusedVariables
class NoUnusedVariables extends AbstractValidationRule
{
static function unusedVariableMessage($varName, $opName = null)
{
@ -17,7 +17,7 @@ class NoUnusedVariables
public $variableDefs;
public function __invoke(ValidationContext $context)
public function getVisitor(ValidationContext $context)
{
$this->variableDefs = [];

View File

@ -23,7 +23,7 @@ use GraphQL\Utils\PairSet;
use GraphQL\Utils\TypeInfo;
use GraphQL\Validator\ValidationContext;
class OverlappingFieldsCanBeMerged
class OverlappingFieldsCanBeMerged extends AbstractValidationRule
{
static function fieldsConflictMessage($responseName, $reason)
{
@ -49,7 +49,7 @@ class OverlappingFieldsCanBeMerged
*/
public $comparedSet;
public function __invoke(ValidationContext $context)
public function getVisitor(ValidationContext $context)
{
$this->comparedSet = new PairSet();

View File

@ -16,7 +16,7 @@ use GraphQL\Type\Definition\UnionType;
use GraphQL\Validator\ValidationContext;
use GraphQL\Utils\TypeInfo;
class PossibleFragmentSpreads
class PossibleFragmentSpreads extends AbstractValidationRule
{
static function typeIncompatibleSpreadMessage($fragName, $parentType, $fragType)
{
@ -28,7 +28,7 @@ class PossibleFragmentSpreads
return "Fragment cannot be spread here as objects of type \"$parentType\" can never be of type \"$fragType\".";
}
public function __invoke(ValidationContext $context)
public function getVisitor(ValidationContext $context)
{
return [
NodeKind::INLINE_FRAGMENT => function(InlineFragmentNode $node) use ($context) {

View File

@ -12,7 +12,7 @@ use GraphQL\Type\Definition\NonNull;
use GraphQL\Utils\Utils;
use GraphQL\Validator\ValidationContext;
class ProvidedNonNullArguments
class ProvidedNonNullArguments extends AbstractValidationRule
{
static function missingFieldArgMessage($fieldName, $argName, $type)
{
@ -24,7 +24,7 @@ class ProvidedNonNullArguments
return "Directive \"@$directiveName\" argument \"$argName\" of type \"$type\" is required but not provided.";
}
public function __invoke(ValidationContext $context)
public function getVisitor(ValidationContext $context)
{
return [
NodeKind::FIELD => [

View File

@ -31,9 +31,9 @@ class QueryComplexity extends AbstractQuerySecurity
*/
private $context;
public function __construct($maxQueryDepth)
public function __construct($maxQueryComplexity)
{
$this->setMaxQueryComplexity($maxQueryDepth);
$this->setMaxQueryComplexity($maxQueryComplexity);
}
public static function maxQueryComplexityErrorMessage($max, $count)
@ -68,7 +68,7 @@ class QueryComplexity extends AbstractQuerySecurity
return $this->rawVariableValues;
}
public function __invoke(ValidationContext $context)
public function getVisitor(ValidationContext $context)
{
$this->context = $context;

View File

@ -45,7 +45,7 @@ class QueryDepth extends AbstractQuerySecurity
return sprintf('Max query depth should be %d but got %d.', $max, $count);
}
public function __invoke(ValidationContext $context)
public function getVisitor(ValidationContext $context)
{
return $this->invokeIfNeeded(
$context,

View File

@ -7,7 +7,7 @@ use GraphQL\Language\AST\NodeKind;
use GraphQL\Type\Definition\Type;
use GraphQL\Validator\ValidationContext;
class ScalarLeafs
class ScalarLeafs extends AbstractValidationRule
{
static function noSubselectionAllowedMessage($field, $type)
{
@ -19,7 +19,7 @@ class ScalarLeafs
return "Field \"$field\" of type \"$type\" must have a sub selection.";
}
public function __invoke(ValidationContext $context)
public function getVisitor(ValidationContext $context)
{
return [
NodeKind::FIELD => function(FieldNode $node) use ($context) {

View File

@ -8,7 +8,7 @@ use GraphQL\Language\AST\NodeKind;
use GraphQL\Language\Visitor;
use GraphQL\Validator\ValidationContext;
class UniqueArgumentNames
class UniqueArgumentNames extends AbstractValidationRule
{
static function duplicateArgMessage($argName)
{
@ -17,7 +17,7 @@ class UniqueArgumentNames
public $knownArgNames;
public function __invoke(ValidationContext $context)
public function getVisitor(ValidationContext $context)
{
$this->knownArgNames = [];

View File

@ -6,14 +6,14 @@ use GraphQL\Language\AST\DirectiveNode;
use GraphQL\Language\AST\Node;
use GraphQL\Validator\ValidationContext;
class UniqueDirectivesPerLocation
class UniqueDirectivesPerLocation extends AbstractValidationRule
{
static function duplicateDirectiveMessage($directiveName)
{
return 'The directive "'.$directiveName.'" can only be used once at this location.';
}
public function __invoke(ValidationContext $context)
public function getVisitor(ValidationContext $context)
{
return [
'enter' => function(Node $node) use ($context) {

View File

@ -9,7 +9,7 @@ use GraphQL\Language\AST\NodeKind;
use GraphQL\Language\Visitor;
use GraphQL\Validator\ValidationContext;
class UniqueFragmentNames
class UniqueFragmentNames extends AbstractValidationRule
{
static function duplicateFragmentNameMessage($fragName)
{
@ -18,7 +18,7 @@ class UniqueFragmentNames
public $knownFragmentNames;
public function __invoke(ValidationContext $context)
public function getVisitor(ValidationContext $context)
{
$this->knownFragmentNames = [];

View File

@ -8,7 +8,7 @@ use GraphQL\Language\AST\ObjectFieldNode;
use GraphQL\Language\Visitor;
use GraphQL\Validator\ValidationContext;
class UniqueInputFieldNames
class UniqueInputFieldNames extends AbstractValidationRule
{
static function duplicateInputFieldMessage($fieldName)
{
@ -18,7 +18,7 @@ class UniqueInputFieldNames
public $knownNames;
public $knownNameStack;
public function __invoke(ValidationContext $context)
public function getVisitor(ValidationContext $context)
{
$this->knownNames = [];
$this->knownNameStack = [];

View File

@ -8,7 +8,7 @@ use GraphQL\Language\AST\OperationDefinitionNode;
use GraphQL\Language\Visitor;
use GraphQL\Validator\ValidationContext;
class UniqueOperationNames
class UniqueOperationNames extends AbstractValidationRule
{
static function duplicateOperationNameMessage($operationName)
{
@ -17,7 +17,7 @@ class UniqueOperationNames
public $knownOperationNames;
public function __invoke(ValidationContext $context)
public function getVisitor(ValidationContext $context)
{
$this->knownOperationNames = [];

View File

@ -7,7 +7,7 @@ use GraphQL\Language\AST\NodeKind;
use GraphQL\Language\AST\VariableDefinitionNode;
use GraphQL\Validator\ValidationContext;
class UniqueVariableNames
class UniqueVariableNames extends AbstractValidationRule
{
static function duplicateVariableMessage($variableName)
{
@ -16,7 +16,7 @@ class UniqueVariableNames
public $knownVariableNames;
public function __invoke(ValidationContext $context)
public function getVisitor(ValidationContext $context)
{
$this->knownVariableNames = [];

View File

@ -12,14 +12,14 @@ use GraphQL\Type\Definition\Type;
use GraphQL\Utils\TypeInfo;
use GraphQL\Validator\ValidationContext;
class VariablesAreInputTypes
class VariablesAreInputTypes extends AbstractValidationRule
{
static function nonInputTypeOnVarMessage($variableName, $typeName)
{
return "Variable \"\$$variableName\" cannot be non-input type \"$typeName\".";
}
public function __invoke(ValidationContext $context)
public function getVisitor(ValidationContext $context)
{
return [
NodeKind::VARIABLE_DEFINITION => function(VariableDefinitionNode $node) use ($context) {

View File

@ -11,7 +11,7 @@ use GraphQL\Utils\TypeComparators;
use GraphQL\Utils\TypeInfo;
use GraphQL\Validator\ValidationContext;
class VariablesInAllowedPosition
class VariablesInAllowedPosition extends AbstractValidationRule
{
static function badVarPosMessage($varName, $varType, $expectedType)
{
@ -21,7 +21,7 @@ class VariablesInAllowedPosition
public $varDefMap;
public function __invoke(ValidationContext $context)
public function getVisitor(ValidationContext $context)
{
return [
NodeKind::OPERATION_DEFINITION => [

View File

@ -17,6 +17,7 @@ use GraphQL\Server\ServerConfig;
use GraphQL\Type\Definition\ObjectType;
use GraphQL\Type\Definition\Type;
use GraphQL\Validator\DocumentValidator;
use GraphQL\Validator\Rules\CustomValidationRule;
use GraphQL\Validator\ValidationContext;
class QueryExecutionTest extends \PHPUnit_Framework_TestCase
@ -219,10 +220,10 @@ class QueryExecutionTest extends \PHPUnit_Framework_TestCase
$called = false;
$rules = [
function() use (&$called) {
new CustomValidationRule('SomeRule', function() use (&$called) {
$called = true;
return [];
}
})
];
$this->config->setValidationRules($rules);
@ -268,9 +269,9 @@ class QueryExecutionTest extends \PHPUnit_Framework_TestCase
} else {
$called2 = true;
return [
function(ValidationContext $context) {
new CustomValidationRule('MyRule', function(ValidationContext $context) {
$context->reportError(new Error("This is the error we are looking for!"));
}
})
];
}
});

View File

@ -5,6 +5,7 @@ use GraphQL\Error\Error;
use GraphQL\Language\AST\NodeKind;
use GraphQL\Language\Parser;
use GraphQL\Validator\DocumentValidator;
use GraphQL\Validator\Rules\CustomValidationRule;
use GraphQL\Validator\Rules\QueryComplexity;
use GraphQL\Validator\ValidationContext;
@ -159,7 +160,7 @@ class QueryComplexityTest extends AbstractQuerySecurityTest
$query = 'query MyQuery { human(name: INVALID_VALUE) { dogs {name} } }';
$reportedError = new Error("OtherValidatorError");
$otherRule = function(ValidationContext $context) use ($reportedError) {
$otherRule = new CustomValidationRule('otherRule', function(ValidationContext $context) use ($reportedError) {
return [
NodeKind::OPERATION_DEFINITION => [
'leave' => function() use ($context, $reportedError) {
@ -167,7 +168,7 @@ class QueryComplexityTest extends AbstractQuerySecurityTest
}
]
];
};
});
$errors = DocumentValidator::validate(
QuerySecuritySchema::buildSchema(),

View File

@ -1,6 +1,9 @@
<?php
namespace GraphQL\Tests\Validator;
use GraphQL\Validator\DocumentValidator;
use GraphQL\Validator\Rules\QueryComplexity;
class ValidationTest extends TestCase
{
// Validate: Supports full validation
@ -23,7 +26,16 @@ class ValidationTest extends TestCase
}
');
}
/*
public function testAllowsSettingRulesGlobally()
{
$rule = new QueryComplexity(0);
DocumentValidator::addRule($rule);
$instance = DocumentValidator::getRule(QueryComplexity::class);
$this->assertSame($rule, $instance);
}
*/
public function testPassesValidationWithEmptyRules()
{
$query = '{invalid}';