mirror of
https://github.com/retailcrm/graphql-php.git
synced 2025-02-05 23:39:26 +03:00
Add experimental support for parsing variable definitions in fragments
ref: graphql/graphql-js#1141
This commit is contained in:
parent
fde7df534d
commit
949b853678
@ -10,13 +10,21 @@ class FragmentDefinitionNode extends Node implements ExecutableDefinitionNode, H
|
||||
*/
|
||||
public $name;
|
||||
|
||||
/**
|
||||
* Note: fragment variable definitions are experimental and may be changed
|
||||
* or removed in the future.
|
||||
*
|
||||
* @var VariableDefinitionNode[]|NodeList
|
||||
*/
|
||||
public $variableDefinitions;
|
||||
|
||||
/**
|
||||
* @var NamedTypeNode
|
||||
*/
|
||||
public $typeCondition;
|
||||
|
||||
/**
|
||||
* @var DirectiveNode[]
|
||||
* @var DirectiveNode[]|NodeList
|
||||
*/
|
||||
public $directives;
|
||||
|
||||
|
@ -49,7 +49,6 @@ use GraphQL\Language\AST\UnionTypeExtensionNode;
|
||||
use GraphQL\Language\AST\VariableNode;
|
||||
use GraphQL\Language\AST\VariableDefinitionNode;
|
||||
use GraphQL\Error\SyntaxError;
|
||||
use GraphQL\Type\TypeKind;
|
||||
|
||||
/**
|
||||
* Parses string containing GraphQL query or [type definition](type-system/type-language.md) to Abstract Syntax Tree.
|
||||
@ -67,10 +66,25 @@ class Parser
|
||||
* in the source that they correspond to. This configuration flag
|
||||
* disables that behavior for performance or testing.)
|
||||
*
|
||||
* experimentalFragmentVariables: boolean,
|
||||
* (If enabled, the parser will understand and parse variable definitions
|
||||
* contained in a fragment definition. They'll be represented in the
|
||||
* `variableDefinitions` field of the FragmentDefinitionNode.
|
||||
*
|
||||
* The syntax is identical to normal, query-defined variables. For example:
|
||||
*
|
||||
* fragment A($var: Boolean = false) on T {
|
||||
* ...
|
||||
* }
|
||||
*
|
||||
* Note: this feature is experimental and may change or be removed in the
|
||||
* future.)
|
||||
*
|
||||
* @api
|
||||
* @param Source|string $source
|
||||
* @param array $options
|
||||
* @return DocumentNode
|
||||
* @throws SyntaxError
|
||||
*/
|
||||
public static function parse($source, array $options = [])
|
||||
{
|
||||
@ -639,11 +653,19 @@ class Parser
|
||||
$this->expectKeyword('fragment');
|
||||
|
||||
$name = $this->parseFragmentName();
|
||||
|
||||
// Experimental support for defining variables within fragments changes
|
||||
// the grammar of FragmentDefinition:
|
||||
// - fragment FragmentName VariableDefinitions? on TypeCondition Directives? SelectionSet
|
||||
$variableDefinitions = null;
|
||||
if (isset($this->lexer->options['experimentalFragmentVariables'])) {
|
||||
$variableDefinitions = $this->parseVariableDefinitions();
|
||||
}
|
||||
$this->expectKeyword('on');
|
||||
$typeCondition = $this->parseNamedType();
|
||||
|
||||
return new FragmentDefinitionNode([
|
||||
'name' => $name,
|
||||
'variableDefinitions' => $variableDefinitions,
|
||||
'typeCondition' => $typeCondition,
|
||||
'directives' => $this->parseDirectives(false),
|
||||
'selectionSet' => $this->parseSelectionSet(),
|
||||
|
@ -131,7 +131,11 @@ class Printer
|
||||
], ' ');
|
||||
},
|
||||
NodeKind::FRAGMENT_DEFINITION => function(FragmentDefinitionNode $node) {
|
||||
return "fragment {$node->name} on {$node->typeCondition} "
|
||||
// Note: fragment variable definitions are experimental and may be changed
|
||||
// or removed in the future.
|
||||
return "fragment {$node->name}"
|
||||
. $this->wrap('(', $this->join($node->variableDefinitions, ', '), ')')
|
||||
. " on {$node->typeCondition} "
|
||||
. $this->wrap('', $this->join($node->directives, ' '), ' ')
|
||||
. $node->selectionSet;
|
||||
},
|
||||
|
@ -115,7 +115,15 @@ class Visitor
|
||||
NodeKind::ARGUMENT => ['name', 'value'],
|
||||
NodeKind::FRAGMENT_SPREAD => ['name', 'directives'],
|
||||
NodeKind::INLINE_FRAGMENT => ['typeCondition', 'directives', 'selectionSet'],
|
||||
NodeKind::FRAGMENT_DEFINITION => ['name', 'typeCondition', 'directives', 'selectionSet'],
|
||||
NodeKind::FRAGMENT_DEFINITION => [
|
||||
'name',
|
||||
// Note: fragment variable definitions are experimental and may be changed
|
||||
// or removed in the future.
|
||||
'variableDefinitions',
|
||||
'typeCondition',
|
||||
'directives',
|
||||
'selectionSet'
|
||||
],
|
||||
|
||||
NodeKind::INT => [],
|
||||
NodeKind::FLOAT => [],
|
||||
|
@ -453,10 +453,23 @@ fragment $fragmentName on Type {
|
||||
$this->assertEquals(null, $result->loc);
|
||||
}
|
||||
|
||||
/**
|
||||
* @it Experimental: allows parsing fragment defined variables
|
||||
*/
|
||||
public function testExperimentalAllowsParsingFragmentDefinedVariables()
|
||||
{
|
||||
$source = new Source('fragment a($v: Boolean = false) on t { f(v: $v) }');
|
||||
// not throw
|
||||
Parser::parse($source, ['experimentalFragmentVariables' => true]);
|
||||
|
||||
$this->setExpectedException(SyntaxError::class);
|
||||
Parser::parse($source);
|
||||
}
|
||||
|
||||
/**
|
||||
* @it contains location information that only stringifys start/end
|
||||
*/
|
||||
public function testConvertToArray()
|
||||
public function testContainsLocationInformationThatOnlyStringifysStartEnd()
|
||||
{
|
||||
$source = new Source('{ id }');
|
||||
$result = Parser::parse($source);
|
||||
|
@ -132,6 +132,28 @@ class PrinterTest extends \PHPUnit_Framework_TestCase
|
||||
$this->assertEquals($expected, Printer::doPrint($mutationAstWithArtifacts));
|
||||
}
|
||||
|
||||
/**
|
||||
* @it Experimental: correctly prints fragment defined variables
|
||||
*/
|
||||
public function testExperimentalCorrectlyPrintsFragmentDefinedVariables()
|
||||
{
|
||||
$fragmentWithVariable = Parser::parse('
|
||||
fragment Foo($a: ComplexType, $b: Boolean = false) on TestType {
|
||||
id
|
||||
}
|
||||
',
|
||||
['experimentalFragmentVariables' => true]
|
||||
);
|
||||
|
||||
$this->assertEquals(
|
||||
Printer::doPrint($fragmentWithVariable),
|
||||
'fragment Foo($a: ComplexType, $b: Boolean = false) on TestType {
|
||||
id
|
||||
}
|
||||
'
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @it correctly prints single-line with leading space and quotation
|
||||
*/
|
||||
|
@ -326,6 +326,60 @@ class VisitorTest extends \PHPUnit_Framework_TestCase
|
||||
$this->assertEquals($expected, $visited);
|
||||
}
|
||||
|
||||
/**
|
||||
* @it Experimental: visits variables defined in fragments
|
||||
*/
|
||||
public function testExperimentalVisitsVariablesDefinedInFragments()
|
||||
{
|
||||
$ast = Parser::parse(
|
||||
'fragment a($v: Boolean = false) on t { f }',
|
||||
['experimentalFragmentVariables' => true]
|
||||
);
|
||||
$visited = [];
|
||||
|
||||
Visitor::visit($ast, [
|
||||
'enter' => function($node) use (&$visited) {
|
||||
$visited[] = ['enter', $node->kind, isset($node->value) ? $node->value : null];
|
||||
},
|
||||
'leave' => function($node) use (&$visited) {
|
||||
$visited[] = ['leave', $node->kind, isset($node->value) ? $node->value : null];
|
||||
},
|
||||
]);
|
||||
|
||||
$expected = [
|
||||
['enter', 'Document', null],
|
||||
['enter', 'FragmentDefinition', null],
|
||||
['enter', 'Name', 'a'],
|
||||
['leave', 'Name', 'a'],
|
||||
['enter', 'VariableDefinition', null],
|
||||
['enter', 'Variable', null],
|
||||
['enter', 'Name', 'v'],
|
||||
['leave', 'Name', 'v'],
|
||||
['leave', 'Variable', null],
|
||||
['enter', 'NamedType', null],
|
||||
['enter', 'Name', 'Boolean'],
|
||||
['leave', 'Name', 'Boolean'],
|
||||
['leave', 'NamedType', null],
|
||||
['enter', 'BooleanValue', false],
|
||||
['leave', 'BooleanValue', false],
|
||||
['leave', 'VariableDefinition', null],
|
||||
['enter', 'NamedType', null],
|
||||
['enter', 'Name', 't'],
|
||||
['leave', 'Name', 't'],
|
||||
['leave', 'NamedType', null],
|
||||
['enter', 'SelectionSet', null],
|
||||
['enter', 'Field', null],
|
||||
['enter', 'Name', 'f'],
|
||||
['leave', 'Name', 'f'],
|
||||
['leave', 'Field', null],
|
||||
['leave', 'SelectionSet', null],
|
||||
['leave', 'FragmentDefinition', null],
|
||||
['leave', 'Document', null],
|
||||
];
|
||||
|
||||
$this->assertEquals($expected, $visited);
|
||||
}
|
||||
|
||||
/**
|
||||
* @it visits kitchen sink
|
||||
*/
|
||||
|
Loading…
x
Reference in New Issue
Block a user