mirror of
https://github.com/retailcrm/graphql-php.git
synced 2024-11-25 06:16:05 +03:00
Merge pull request #360 from bytorsten/expand-extension
Add missing extensionASTNodes
This commit is contained in:
commit
aed406eade
@ -69,6 +69,9 @@ class NodeKind
|
||||
|
||||
const DIRECTIVE_DEFINITION = 'DirectiveDefinition';
|
||||
|
||||
// Type System Extensions
|
||||
const SCHEMA_EXTENSION = 'SchemaExtension';
|
||||
|
||||
/** @var string[] */
|
||||
public static $classMap = [
|
||||
self::NAME => NameNode::class,
|
||||
|
17
src/Language/AST/SchemaTypeExtensionNode.php
Normal file
17
src/Language/AST/SchemaTypeExtensionNode.php
Normal file
@ -0,0 +1,17 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace GraphQL\Language\AST;
|
||||
|
||||
class SchemaTypeExtensionNode extends Node implements TypeExtensionNode
|
||||
{
|
||||
/** @var string */
|
||||
public $kind = NodeKind::SCHEMA_EXTENSION;
|
||||
|
||||
/** @var DirectiveNode[]|null */
|
||||
public $directives;
|
||||
|
||||
/** @var OperationTypeDefinitionNode[]|null */
|
||||
public $operationTypes;
|
||||
}
|
@ -44,6 +44,7 @@ use GraphQL\Language\AST\OperationTypeDefinitionNode;
|
||||
use GraphQL\Language\AST\ScalarTypeDefinitionNode;
|
||||
use GraphQL\Language\AST\ScalarTypeExtensionNode;
|
||||
use GraphQL\Language\AST\SchemaDefinitionNode;
|
||||
use GraphQL\Language\AST\SchemaTypeExtensionNode;
|
||||
use GraphQL\Language\AST\SelectionSetNode;
|
||||
use GraphQL\Language\AST\StringValueNode;
|
||||
use GraphQL\Language\AST\TypeExtensionNode;
|
||||
@ -1446,6 +1447,8 @@ class Parser
|
||||
|
||||
if ($keywordToken->kind === Token::NAME) {
|
||||
switch ($keywordToken->value) {
|
||||
case 'schema':
|
||||
return $this->parseSchemaTypeExtension();
|
||||
case 'scalar':
|
||||
return $this->parseScalarTypeExtension();
|
||||
case 'type':
|
||||
@ -1464,6 +1467,33 @@ class Parser
|
||||
throw $this->unexpected($keywordToken);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return SchemaTypeExtensionNode
|
||||
* @throws SyntaxError
|
||||
*/
|
||||
private function parseSchemaTypeExtension()
|
||||
{
|
||||
$start = $this->lexer->token;
|
||||
$this->expectKeyword('extend');
|
||||
$this->expectKeyword('schema');
|
||||
$directives = $this->parseDirectives(true);
|
||||
$operationTypes = $this->peek(Token::BRACE_L)
|
||||
? $this->many(
|
||||
Token::BRACE_L,
|
||||
[$this, 'parseOperationTypeDefinition'],
|
||||
Token::BRACE_R
|
||||
) : [];
|
||||
if (count($directives) === 0 && count($operationTypes) === 0) {
|
||||
$this->unexpected();
|
||||
}
|
||||
|
||||
return new SchemaTypeExtensionNode([
|
||||
'directives' => $directives,
|
||||
'operationTypes' => $operationTypes,
|
||||
'loc' => $this->loc($start),
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return ScalarTypeExtensionNode
|
||||
* @throws SyntaxError
|
||||
|
@ -41,6 +41,7 @@ use GraphQL\Language\AST\OperationTypeDefinitionNode;
|
||||
use GraphQL\Language\AST\ScalarTypeDefinitionNode;
|
||||
use GraphQL\Language\AST\ScalarTypeExtensionNode;
|
||||
use GraphQL\Language\AST\SchemaDefinitionNode;
|
||||
use GraphQL\Language\AST\SchemaTypeExtensionNode;
|
||||
use GraphQL\Language\AST\SelectionSetNode;
|
||||
use GraphQL\Language\AST\StringValueNode;
|
||||
use GraphQL\Language\AST\UnionTypeDefinitionNode;
|
||||
@ -335,6 +336,17 @@ class Printer
|
||||
);
|
||||
}),
|
||||
|
||||
NodeKind::SCHEMA_EXTENSION => function (SchemaTypeExtensionNode $def) {
|
||||
return $this->join(
|
||||
[
|
||||
'extend schema',
|
||||
$this->join($def->directives, ' '),
|
||||
$this->block($def->operationTypes),
|
||||
],
|
||||
' '
|
||||
);
|
||||
},
|
||||
|
||||
NodeKind::SCALAR_TYPE_EXTENSION => function (ScalarTypeExtensionNode $def) {
|
||||
return $this->join(
|
||||
[
|
||||
|
@ -165,6 +165,8 @@ class Visitor
|
||||
NodeKind::INPUT_OBJECT_TYPE_EXTENSION => ['name', 'directives', 'fields'],
|
||||
|
||||
NodeKind::DIRECTIVE_DEFINITION => ['description', 'name', 'arguments', 'locations'],
|
||||
|
||||
NodeKind::SCHEMA_EXTENSION => ['directives', 'operationTypes'],
|
||||
];
|
||||
|
||||
/**
|
||||
|
@ -50,7 +50,7 @@ class Directive
|
||||
$args = [];
|
||||
foreach ($config['args'] as $name => $arg) {
|
||||
if (is_array($arg)) {
|
||||
$args[] = FieldDefinition::create($arg + ['name' => $name]);
|
||||
$args[] = new FieldArgument($arg + ['name' => $name]);
|
||||
} else {
|
||||
$args[] = $arg;
|
||||
}
|
||||
|
@ -7,6 +7,7 @@ namespace GraphQL\Type\Definition;
|
||||
use GraphQL\Error\Error;
|
||||
use GraphQL\Error\InvariantViolation;
|
||||
use GraphQL\Language\AST\EnumTypeDefinitionNode;
|
||||
use GraphQL\Language\AST\EnumTypeExtensionNode;
|
||||
use GraphQL\Language\AST\EnumValueNode;
|
||||
use GraphQL\Language\AST\Node;
|
||||
use GraphQL\Utils\MixedStore;
|
||||
@ -33,6 +34,9 @@ class EnumType extends Type implements InputType, OutputType, LeafType, NamedTyp
|
||||
/** @var \ArrayObject<string, EnumValueDefinition> */
|
||||
private $nameLookup;
|
||||
|
||||
/** @var EnumTypeExtensionNode[] */
|
||||
public $extensionASTNodes;
|
||||
|
||||
public function __construct($config)
|
||||
{
|
||||
if (! isset($config['name'])) {
|
||||
@ -44,6 +48,7 @@ class EnumType extends Type implements InputType, OutputType, LeafType, NamedTyp
|
||||
$this->name = $config['name'];
|
||||
$this->description = $config['description'] ?? null;
|
||||
$this->astNode = $config['astNode'] ?? null;
|
||||
$this->extensionASTNodes = $config['extensionASTNodes'] ?? null;
|
||||
$this->config = $config;
|
||||
}
|
||||
|
||||
|
@ -6,6 +6,7 @@ namespace GraphQL\Type\Definition;
|
||||
|
||||
use GraphQL\Error\InvariantViolation;
|
||||
use GraphQL\Language\AST\InputObjectTypeDefinitionNode;
|
||||
use GraphQL\Language\AST\InputObjectTypeExtensionNode;
|
||||
use GraphQL\Utils\Utils;
|
||||
use function call_user_func;
|
||||
use function is_array;
|
||||
@ -24,6 +25,9 @@ class InputObjectType extends Type implements InputType, NamedType
|
||||
/** @var InputObjectField[] */
|
||||
private $fields;
|
||||
|
||||
/** @var InputObjectTypeExtensionNode[] */
|
||||
public $extensionASTNodes;
|
||||
|
||||
/**
|
||||
*
|
||||
* @param mixed[] $config
|
||||
@ -40,6 +44,7 @@ class InputObjectType extends Type implements InputType, NamedType
|
||||
$this->name = $config['name'];
|
||||
$this->astNode = $config['astNode'] ?? null;
|
||||
$this->description = $config['description'] ?? null;
|
||||
$this->extensionASTNodes = $config['extensionASTNodes'] ?? null;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -5,6 +5,7 @@ declare(strict_types=1);
|
||||
namespace GraphQL\Type\Definition;
|
||||
|
||||
use GraphQL\Language\AST\ScalarTypeDefinitionNode;
|
||||
use GraphQL\Language\AST\ScalarTypeExtensionNode;
|
||||
use GraphQL\Utils\Utils;
|
||||
use function is_string;
|
||||
|
||||
@ -31,6 +32,9 @@ abstract class ScalarType extends Type implements OutputType, InputType, LeafTyp
|
||||
/** @var ScalarTypeDefinitionNode|null */
|
||||
public $astNode;
|
||||
|
||||
/** @var ScalarTypeExtensionNode[] */
|
||||
public $extensionASTNodes;
|
||||
|
||||
/**
|
||||
* @param mixed[] $config
|
||||
*/
|
||||
@ -39,6 +43,7 @@ abstract class ScalarType extends Type implements OutputType, InputType, LeafTyp
|
||||
$this->name = $config['name'] ?? $this->tryInferName();
|
||||
$this->description = $config['description'] ?? $this->description;
|
||||
$this->astNode = $config['astNode'] ?? null;
|
||||
$this->extensionASTNodes = $config['extensionASTNodes'] ?? null;
|
||||
$this->config = $config;
|
||||
|
||||
Utils::invariant(is_string($this->name), 'Must provide name.');
|
||||
|
@ -6,6 +6,7 @@ namespace GraphQL\Type\Definition;
|
||||
|
||||
use GraphQL\Error\InvariantViolation;
|
||||
use GraphQL\Language\AST\UnionTypeDefinitionNode;
|
||||
use GraphQL\Language\AST\UnionTypeExtensionNode;
|
||||
use GraphQL\Utils\Utils;
|
||||
use function call_user_func;
|
||||
use function is_array;
|
||||
@ -27,6 +28,9 @@ class UnionType extends Type implements AbstractType, OutputType, CompositeType,
|
||||
/** @var ObjectType[] */
|
||||
private $possibleTypeNames;
|
||||
|
||||
/** @var UnionTypeExtensionNode[] */
|
||||
public $extensionASTNodes;
|
||||
|
||||
public function __construct($config)
|
||||
{
|
||||
if (! isset($config['name'])) {
|
||||
@ -43,6 +47,7 @@ class UnionType extends Type implements AbstractType, OutputType, CompositeType,
|
||||
$this->name = $config['name'];
|
||||
$this->description = $config['description'] ?? null;
|
||||
$this->astNode = $config['astNode'] ?? null;
|
||||
$this->extensionASTNodes = $config['extensionASTNodes'] ?? null;
|
||||
$this->config = $config;
|
||||
}
|
||||
|
||||
|
@ -8,6 +8,7 @@ use GraphQL\Error\Error;
|
||||
use GraphQL\Error\InvariantViolation;
|
||||
use GraphQL\GraphQL;
|
||||
use GraphQL\Language\AST\SchemaDefinitionNode;
|
||||
use GraphQL\Language\AST\SchemaTypeExtensionNode;
|
||||
use GraphQL\Type\Definition\AbstractType;
|
||||
use GraphQL\Type\Definition\Directive;
|
||||
use GraphQL\Type\Definition\InterfaceType;
|
||||
@ -67,6 +68,9 @@ class Schema
|
||||
/** @var InvariantViolation[]|null */
|
||||
private $validationErrors;
|
||||
|
||||
/** @var SchemaTypeExtensionNode[] */
|
||||
public $extensionASTNodes;
|
||||
|
||||
/**
|
||||
* @api
|
||||
* @param mixed[]|SchemaConfig $config
|
||||
@ -111,6 +115,8 @@ class Schema
|
||||
}
|
||||
|
||||
$this->config = $config;
|
||||
$this->extensionASTNodes = $config->extensionASTNodes;
|
||||
|
||||
if ($config->query) {
|
||||
$this->resolvedTypes[$config->query->name] = $config->query;
|
||||
}
|
||||
|
@ -5,6 +5,7 @@ declare(strict_types=1);
|
||||
namespace GraphQL\Type;
|
||||
|
||||
use GraphQL\Language\AST\SchemaDefinitionNode;
|
||||
use GraphQL\Language\AST\SchemaTypeExtensionNode;
|
||||
use GraphQL\Type\Definition\Directive;
|
||||
use GraphQL\Type\Definition\ObjectType;
|
||||
use GraphQL\Type\Definition\Type;
|
||||
@ -51,6 +52,9 @@ class SchemaConfig
|
||||
/** @var bool */
|
||||
public $assumeValid;
|
||||
|
||||
/** @var SchemaTypeExtensionNode[] */
|
||||
public $extensionASTNodes;
|
||||
|
||||
/**
|
||||
* Converts an array of options to instance of SchemaConfig
|
||||
* (or just returns empty config when array is not passed).
|
||||
@ -100,6 +104,10 @@ class SchemaConfig
|
||||
if (isset($options['assumeValid'])) {
|
||||
$config->setAssumeValid((bool) $options['assumeValid']);
|
||||
}
|
||||
|
||||
if (isset($options['extensionASTNodes'])) {
|
||||
$config->setExtensionASTNodes($options['extensionASTNodes']);
|
||||
}
|
||||
}
|
||||
|
||||
return $config;
|
||||
@ -266,4 +274,20 @@ class SchemaConfig
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return SchemaTypeExtensionNode[]
|
||||
*/
|
||||
public function getExtensionASTNodes()
|
||||
{
|
||||
return $this->extensionASTNodes;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param SchemaTypeExtensionNode[] $extensionASTNodes
|
||||
*/
|
||||
public function setExtensionASTNodes(array $extensionASTNodes)
|
||||
{
|
||||
$this->extensionASTNodes = $extensionASTNodes;
|
||||
}
|
||||
}
|
||||
|
@ -5,6 +5,7 @@ declare(strict_types=1);
|
||||
namespace GraphQL\Validator\Rules;
|
||||
|
||||
use GraphQL\Error\Error;
|
||||
use GraphQL\Language\AST\DirectiveDefinitionNode;
|
||||
use GraphQL\Language\AST\DirectiveNode;
|
||||
use GraphQL\Language\AST\InputObjectTypeDefinitionNode;
|
||||
use GraphQL\Language\AST\Node;
|
||||
@ -12,6 +13,7 @@ use GraphQL\Language\AST\NodeKind;
|
||||
use GraphQL\Language\AST\NodeList;
|
||||
use GraphQL\Language\DirectiveLocation;
|
||||
use GraphQL\Validator\ValidationContext;
|
||||
use function array_map;
|
||||
use function count;
|
||||
use function in_array;
|
||||
use function sprintf;
|
||||
@ -20,37 +22,49 @@ class KnownDirectives extends ValidationRule
|
||||
{
|
||||
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();
|
||||
$definedDirectives = $schema->getDirectives();
|
||||
|
||||
foreach ($definedDirectives as $directive) {
|
||||
$locationsMap[$directive->name] = $directive->locations;
|
||||
}
|
||||
|
||||
if (! $directiveDef) {
|
||||
$astDefinition = $context->getDocument()->definitions;
|
||||
|
||||
foreach ($astDefinition as $def) {
|
||||
if (! ($def instanceof DirectiveDefinitionNode)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$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) {
|
||||
$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),
|
||||
[$node]
|
||||
));
|
||||
if (! $candidateLocation || in_array($candidateLocation, $locations)) {
|
||||
return;
|
||||
}
|
||||
$context->reportError(
|
||||
new Error(
|
||||
self::misplacedDirectiveMessage($name, $candidateLocation),
|
||||
[$node]
|
||||
)
|
||||
);
|
||||
},
|
||||
];
|
||||
}
|
||||
@ -88,6 +102,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:
|
||||
|
Loading…
Reference in New Issue
Block a user