diff --git a/src/Validator/Rules/KnownDirectives.php b/src/Validator/Rules/KnownDirectives.php index e9a0b8c..519f50c 100644 --- a/src/Validator/Rules/KnownDirectives.php +++ b/src/Validator/Rules/KnownDirectives.php @@ -11,6 +11,7 @@ use GraphQL\Language\AST\Node; use GraphQL\Language\AST\NodeKind; use GraphQL\Language\AST\NodeList; use GraphQL\Language\DirectiveLocation; +use GraphQL\Type\Definition\Directive; use GraphQL\Validator\ValidationContext; use function count; use function in_array; @@ -18,40 +19,51 @@ use function sprintf; class KnownDirectives extends ValidationRule { + /** + * @param ValidationContext $context + * @return array + */ public function getVisitor(ValidationContext $context) { - return [ - NodeKind::DIRECTIVE => function (DirectiveNode $node, $key, $parent, $path, $ancestors) use ($context) { - $directiveDef = null; - foreach ($context->getSchema()->getDirectives() as $def) { - if ($def->name === $node->name->value) { - $directiveDef = $def; - break; - } - } + $locationsMap = []; + $schema = $context->getSchema(); - if (! $directiveDef) { + $definedDirectives = $schema ? $schema->getDirectives() : Directive::getInternalDirectives(); + + foreach ($definedDirectives as $directive) { + $locationsMap[$directive->name] = $directive->locations; + } + + $astDefinition = $context->getDocument()->definitions; + foreach ($astDefinition as $def) { + if ($def->kind === NodeKind::DIRECTIVE_DEFINITION) { + $locationsMap[$def->name->value] = array_map(function ($name) { + return $name->value; + }, $def->locations); + } + } + return [ + NodeKind::DIRECTIVE => function (DirectiveNode $node, $key, $parent, $path, $ancestors) use ($context, $locationsMap) { + $name = $node->name->value; + $locations = $locationsMap[$name] ?? null; + + if (!$locations) { $context->reportError(new Error( - self::unknownDirectiveMessage($node->name->value), + self::unknownDirectiveMessage($name), [$node] )); - return; } + $candidateLocation = $this->getDirectiveLocationForASTPath($ancestors); - if (! $candidateLocation) { + if ($candidateLocation && !in_array($candidateLocation, $locations)) { $context->reportError(new Error( - self::misplacedDirectiveMessage($node->name->value, $node->type), - [$node] - )); - } elseif (! in_array($candidateLocation, $directiveDef->locations)) { - $context->reportError(new Error( - self::misplacedDirectiveMessage($node->name->value, $candidateLocation), + self::misplacedDirectiveMessage($name, $candidateLocation), [$node] )); } - }, + } ]; } @@ -88,6 +100,7 @@ class KnownDirectives extends ValidationRule case NodeKind::FRAGMENT_DEFINITION: return DirectiveLocation::FRAGMENT_DEFINITION; case NodeKind::SCHEMA_DEFINITION: + case NodeKind::SCHEMA_EXTENSION: return DirectiveLocation::SCHEMA; case NodeKind::SCALAR_TYPE_DEFINITION: case NodeKind::SCALAR_TYPE_EXTENSION: