diff --git a/src/Language/AST/NodeKind.php b/src/Language/AST/NodeKind.php index d321cdc..7c0f300 100644 --- a/src/Language/AST/NodeKind.php +++ b/src/Language/AST/NodeKind.php @@ -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, diff --git a/src/Language/AST/SchemaTypeExtensionNode.php b/src/Language/AST/SchemaTypeExtensionNode.php new file mode 100644 index 0000000..e86177d --- /dev/null +++ b/src/Language/AST/SchemaTypeExtensionNode.php @@ -0,0 +1,17 @@ +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 diff --git a/src/Language/Printer.php b/src/Language/Printer.php index f5110f1..7c3bbb4 100644 --- a/src/Language/Printer.php +++ b/src/Language/Printer.php @@ -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( [ diff --git a/src/Language/Visitor.php b/src/Language/Visitor.php index 94c83e7..6487913 100644 --- a/src/Language/Visitor.php +++ b/src/Language/Visitor.php @@ -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'] ]; /** diff --git a/src/Type/Schema.php b/src/Type/Schema.php index 4d86298..a3f3294 100644 --- a/src/Type/Schema.php +++ b/src/Type/Schema.php @@ -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; } diff --git a/src/Type/SchemaConfig.php b/src/Type/SchemaConfig.php index 3be1c98..142bafd 100644 --- a/src/Type/SchemaConfig.php +++ b/src/Type/SchemaConfig.php @@ -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(): array + { + return $this->extensionASTNodes; + } + + /** + * @param SchemaTypeExtensionNode[] $extensionASTNodes + */ + public function setExtensionASTNodes(array $extensionASTNodes): void + { + $this->extensionASTNodes = $extensionASTNodes; + } }