Added validation rule for unique directives per location

This commit is contained in:
vladar 2016-11-19 00:21:56 +07:00
parent 8a676cde99
commit f672f0c90c
3 changed files with 178 additions and 0 deletions

View File

@ -43,6 +43,7 @@ use GraphQL\Validator\Rules\QueryComplexity;
use GraphQL\Validator\Rules\QueryDepth;
use GraphQL\Validator\Rules\ScalarLeafs;
use GraphQL\Validator\Rules\UniqueArgumentNames;
use GraphQL\Validator\Rules\UniqueDirectivesPerLocation;
use GraphQL\Validator\Rules\UniqueFragmentNames;
use GraphQL\Validator\Rules\UniqueInputFieldNames;
use GraphQL\Validator\Rules\UniqueOperationNames;
@ -88,6 +89,7 @@ class DocumentValidator
'NoUndefinedVariables' => new NoUndefinedVariables(),
'NoUnusedVariables' => new NoUnusedVariables(),
'KnownDirectives' => new KnownDirectives(),
'UniqueDirectivesPerLocation' => new UniqueDirectivesPerLocation(),
'KnownArgumentNames' => new KnownArgumentNames(),
'UniqueArgumentNames' => new UniqueArgumentNames(),
'ArgumentsOfCorrectType' => new ArgumentsOfCorrectType(),

View File

@ -0,0 +1,38 @@
<?php
namespace GraphQL\Validator\Rules;
use GraphQL\Error\Error;
use GraphQL\Language\AST\Directive;
use GraphQL\Language\AST\Node;
use GraphQL\Validator\ValidationContext;
class UniqueDirectivesPerLocation
{
static function duplicateDirectiveMessage($directiveName)
{
return 'The directive "'.$directiveName.'" can only be used once at this location.';
}
public function __invoke(ValidationContext $context)
{
return [
'enter' => function(Node $node) use ($context) {
if (isset($node->directives)) {
$knownDirectives = [];
foreach ($node->directives as $directive) {
/** @var Directive $directive */
$directiveName = $directive->name->value;
if (isset($knownDirectives[$directiveName])) {
$context->reportError(new Error(
self::duplicateDirectiveMessage($directiveName),
[$knownDirectives[$directiveName], $directive]
));
} else {
$knownDirectives[$directiveName] = $directive;
}
}
}
}
];
}
}

View File

@ -0,0 +1,138 @@
<?php
namespace GraphQL\Tests\Validator;
use GraphQL\Validator\Rules\UniqueDirectivesPerLocation;
class UniqueDirectivesPerLocationTest extends TestCase
{
/**
* @it no directives
*/
public function testNoDirectives()
{
$this->expectPassesRule(new UniqueDirectivesPerLocation(), '
fragment Test on Type {
field
}
');
}
/**
* @it unique directives in different locations
*/
public function testUniqueDirectivesInDifferentLocations()
{
$this->expectPassesRule(new UniqueDirectivesPerLocation(), '
fragment Test on Type @directiveA {
field @directiveB
}
');
}
/**
* @it unique directives in same locations
*/
public function testUniqueDirectivesInSameLocations()
{
$this->expectPassesRule(new UniqueDirectivesPerLocation(), '
fragment Test on Type @directiveA @directiveB {
field @directiveA @directiveB
}
');
}
/**
* @it same directives in different locations
*/
public function testSameDirectivesInDifferentLocations()
{
$this->expectPassesRule(new UniqueDirectivesPerLocation(), '
fragment Test on Type @directiveA {
field @directiveA
}
');
}
/**
* @it same directives in similar locations
*/
public function testSameDirectivesInSimilarLocations()
{
$this->expectPassesRule(new UniqueDirectivesPerLocation(), '
fragment Test on Type {
field @directive
field @directive
}
');
}
/**
* @it duplicate directives in one location
*/
public function testDuplicateDirectivesInOneLocation()
{
$this->expectFailsRule(new UniqueDirectivesPerLocation(), '
fragment Test on Type {
field @directive @directive
}
', [
$this->duplicateDirective('directive', 3, 15, 3, 26)
]);
}
/**
* @it many duplicate directives in one location
*/
public function testManyDuplicateDirectivesInOneLocation()
{
$this->expectFailsRule(new UniqueDirectivesPerLocation(), '
fragment Test on Type {
field @directive @directive @directive
}
', [
$this->duplicateDirective('directive', 3, 15, 3, 26),
$this->duplicateDirective('directive', 3, 15, 3, 37)
]);
}
/**
* @it different duplicate directives in one location
*/
public function testDifferentDuplicateDirectivesInOneLocation()
{
$this->expectFailsRule(new UniqueDirectivesPerLocation, '
fragment Test on Type {
field @directiveA @directiveB @directiveA @directiveB
}
', [
$this->duplicateDirective('directiveA', 3, 15, 3, 39),
$this->duplicateDirective('directiveB', 3, 27, 3, 51)
]);
}
/**
* @it duplicate directives in many locations
*/
public function testDuplicateDirectivesInManyLocations()
{
$this->expectFailsRule(new UniqueDirectivesPerLocation(), '
fragment Test on Type @directive @directive {
field @directive @directive
}
', [
$this->duplicateDirective('directive', 2, 29, 2, 40),
$this->duplicateDirective('directive', 3, 15, 3, 26)
]);
}
private function duplicateDirective($directiveName, $l1, $c1, $l2, $c2)
{
return [
'message' =>UniqueDirectivesPerLocation::duplicateDirectiveMessage($directiveName),
'locations' => [
['line' => $l1, 'column' => $c1],
['line' => $l2, 'column' => $c2]
]
];
}
}