SDL Spec changes

This adds the recent changes to the SDL proposal.

ref: graphql/graphql-js#1117
This commit is contained in:
Daniel Tschinder 2018-02-11 21:08:53 +01:00
parent 74854d55a0
commit b5106a06c9
20 changed files with 580 additions and 116 deletions

View File

@ -19,7 +19,7 @@ class EnumTypeDefinitionNode extends Node implements TypeDefinitionNode
public $directives; public $directives;
/** /**
* @var EnumValueDefinitionNode[] * @var EnumValueDefinitionNode[]|null
*/ */
public $values; public $values;

View File

@ -0,0 +1,25 @@
<?php
namespace GraphQL\Language\AST;
class EnumTypeExtensionNode extends Node implements TypeExtensionNode
{
/**
* @var string
*/
public $kind = NodeKind::ENUM_TYPE_EXTENSION;
/**
* @var NameNode
*/
public $name;
/**
* @var DirectiveNode[]|null
*/
public $directives;
/**
* @var EnumValueDefinitionNode[]|null
*/
public $values;
}

View File

@ -14,12 +14,12 @@ class InputObjectTypeDefinitionNode extends Node implements TypeDefinitionNode
public $name; public $name;
/** /**
* @var DirectiveNode[] * @var DirectiveNode[]|null
*/ */
public $directives; public $directives;
/** /**
* @var InputValueDefinitionNode[] * @var InputValueDefinitionNode[]|null
*/ */
public $fields; public $fields;

View File

@ -0,0 +1,25 @@
<?php
namespace GraphQL\Language\AST;
class InputObjectTypeExtensionNode extends Node implements TypeExtensionNode
{
/**
* @var string
*/
public $kind = NodeKind::INPUT_OBJECT_TYPE_EXTENSION;
/**
* @var NameNode
*/
public $name;
/**
* @var DirectiveNode[]|null
*/
public $directives;
/**
* @var InputValueDefinitionNode[]|null
*/
public $fields;
}

View File

@ -14,14 +14,14 @@ class InterfaceTypeDefinitionNode extends Node implements TypeDefinitionNode
public $name; public $name;
/** /**
* @var DirectiveNode[] * @var DirectiveNode[]|null
*/ */
public $directives; public $directives;
/** /**
* @var FieldDefinitionNode[] * @var FieldDefinitionNode[]|null
*/ */
public $fields = []; public $fields;
/** /**
* @var StringValueNode|null * @var StringValueNode|null

View File

@ -0,0 +1,25 @@
<?php
namespace GraphQL\Language\AST;
class InterfaceTypeExtensionNode extends Node implements TypeExtensionNode
{
/**
* @var string
*/
public $kind = NodeKind::INTERFACE_TYPE_EXTENSION;
/**
* @var NameNode
*/
public $name;
/**
* @var DirectiveNode[]|null
*/
public $directives;
/**
* @var FieldDefinitionNode[]|null
*/
public $fields;
}

View File

@ -65,7 +65,12 @@ class NodeKind
// Type Extensions // Type Extensions
const SCALAR_TYPE_EXTENSION = 'ScalarTypeExtension';
const OBJECT_TYPE_EXTENSION = 'ObjectTypeExtension'; const OBJECT_TYPE_EXTENSION = 'ObjectTypeExtension';
const INTERFACE_TYPE_EXTENSION = 'InterfaceTypeExtension';
const UNION_TYPE_EXTENSION = 'UnionTypeExtension';
const ENUM_TYPE_EXTENSION = 'EnumTypeExtension';
const INPUT_OBJECT_TYPE_EXTENSION = 'InputObjectTypeExtension';
// Directive Definitions // Directive Definitions
@ -127,7 +132,12 @@ class NodeKind
NodeKind::INPUT_OBJECT_TYPE_DEFINITION =>InputObjectTypeDefinitionNode::class, NodeKind::INPUT_OBJECT_TYPE_DEFINITION =>InputObjectTypeDefinitionNode::class,
// Type Extensions // Type Extensions
NodeKind::SCALAR_TYPE_EXTENSION => ScalarTypeExtensionNode::class,
NodeKind::OBJECT_TYPE_EXTENSION => ObjectTypeExtensionNode::class, NodeKind::OBJECT_TYPE_EXTENSION => ObjectTypeExtensionNode::class,
NodeKind::INTERFACE_TYPE_EXTENSION => InterfaceTypeExtensionNode::class,
NodeKind::UNION_TYPE_EXTENSION => UnionTypeExtensionNode::class,
NodeKind::ENUM_TYPE_EXTENSION => EnumTypeExtensionNode::class,
NodeKind::INPUT_OBJECT_TYPE_EXTENSION => InputObjectTypeExtensionNode::class,
// Directive Definitions // Directive Definitions
NodeKind::DIRECTIVE_DEFINITION => DirectiveDefinitionNode::class NodeKind::DIRECTIVE_DEFINITION => DirectiveDefinitionNode::class

View File

@ -19,12 +19,12 @@ class ObjectTypeDefinitionNode extends Node implements TypeDefinitionNode
public $interfaces = []; public $interfaces = [];
/** /**
* @var DirectiveNode[] * @var DirectiveNode[]|null
*/ */
public $directives; public $directives;
/** /**
* @var FieldDefinitionNode[] * @var FieldDefinitionNode[]|null
*/ */
public $fields; public $fields;

View File

@ -0,0 +1,20 @@
<?php
namespace GraphQL\Language\AST;
class ScalarTypeExtensionNode extends Node implements TypeExtensionNode
{
/**
* @var string
*/
public $kind = NodeKind::SCALAR_TYPE_EXTENSION;
/**
* @var NameNode
*/
public $name;
/**
* @var DirectiveNode[]|null
*/
public $directives;
}

View File

@ -5,6 +5,11 @@ interface TypeExtensionNode extends TypeSystemDefinitionNode
{ {
/** /**
export type TypeExtensionNode = export type TypeExtensionNode =
| ObjectTypeExtensionNode; | ScalarTypeExtensionNode
| ObjectTypeExtensionNode
| InterfaceTypeExtensionNode
| UnionTypeExtensionNode
| EnumTypeExtensionNode
| InputObjectTypeExtensionNode;
*/ */
} }

View File

@ -19,9 +19,9 @@ class UnionTypeDefinitionNode extends Node implements TypeDefinitionNode
public $directives; public $directives;
/** /**
* @var NamedTypeNode[] * @var NamedTypeNode[]|null
*/ */
public $types = []; public $types;
/** /**
* @var StringValueNode|null * @var StringValueNode|null

View File

@ -0,0 +1,25 @@
<?php
namespace GraphQL\Language\AST;
class UnionTypeExtensionNode extends Node implements TypeExtensionNode
{
/**
* @var string
*/
public $kind = NodeKind::UNION_TYPE_EXTENSION;
/**
* @var NameNode
*/
public $name;
/**
* @var DirectiveNode[]|null
*/
public $directives;
/**
* @var NamedTypeNode[]|null
*/
public $types;
}

View File

@ -4,11 +4,14 @@ namespace GraphQL\Language;
use GraphQL\Language\AST\ArgumentNode; use GraphQL\Language\AST\ArgumentNode;
use GraphQL\Language\AST\DirectiveDefinitionNode; use GraphQL\Language\AST\DirectiveDefinitionNode;
use GraphQL\Language\AST\EnumTypeDefinitionNode; use GraphQL\Language\AST\EnumTypeDefinitionNode;
use GraphQL\Language\AST\EnumTypeExtensionNode;
use GraphQL\Language\AST\EnumValueDefinitionNode; use GraphQL\Language\AST\EnumValueDefinitionNode;
use GraphQL\Language\AST\FieldDefinitionNode; use GraphQL\Language\AST\FieldDefinitionNode;
use GraphQL\Language\AST\InputObjectTypeDefinitionNode; use GraphQL\Language\AST\InputObjectTypeDefinitionNode;
use GraphQL\Language\AST\InputObjectTypeExtensionNode;
use GraphQL\Language\AST\InputValueDefinitionNode; use GraphQL\Language\AST\InputValueDefinitionNode;
use GraphQL\Language\AST\InterfaceTypeDefinitionNode; use GraphQL\Language\AST\InterfaceTypeDefinitionNode;
use GraphQL\Language\AST\InterfaceTypeExtensionNode;
use GraphQL\Language\AST\ListValueNode; use GraphQL\Language\AST\ListValueNode;
use GraphQL\Language\AST\BooleanValueNode; use GraphQL\Language\AST\BooleanValueNode;
use GraphQL\Language\AST\DirectiveNode; use GraphQL\Language\AST\DirectiveNode;
@ -33,6 +36,7 @@ use GraphQL\Language\AST\ObjectValueNode;
use GraphQL\Language\AST\OperationDefinitionNode; use GraphQL\Language\AST\OperationDefinitionNode;
use GraphQL\Language\AST\OperationTypeDefinitionNode; use GraphQL\Language\AST\OperationTypeDefinitionNode;
use GraphQL\Language\AST\ScalarTypeDefinitionNode; use GraphQL\Language\AST\ScalarTypeDefinitionNode;
use GraphQL\Language\AST\ScalarTypeExtensionNode;
use GraphQL\Language\AST\SchemaDefinitionNode; use GraphQL\Language\AST\SchemaDefinitionNode;
use GraphQL\Language\AST\SelectionSetNode; use GraphQL\Language\AST\SelectionSetNode;
use GraphQL\Language\AST\StringValueNode; use GraphQL\Language\AST\StringValueNode;
@ -40,9 +44,11 @@ use GraphQL\Language\AST\ObjectTypeExtensionNode;
use GraphQL\Language\AST\TypeExtensionNode; use GraphQL\Language\AST\TypeExtensionNode;
use GraphQL\Language\AST\TypeSystemDefinitionNode; use GraphQL\Language\AST\TypeSystemDefinitionNode;
use GraphQL\Language\AST\UnionTypeDefinitionNode; use GraphQL\Language\AST\UnionTypeDefinitionNode;
use GraphQL\Language\AST\UnionTypeExtensionNode;
use GraphQL\Language\AST\VariableNode; use GraphQL\Language\AST\VariableNode;
use GraphQL\Language\AST\VariableDefinitionNode; use GraphQL\Language\AST\VariableDefinitionNode;
use GraphQL\Error\SyntaxError; 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. * Parses string containing GraphQL query or [type definition](type-system/type-language.md) to Abstract Syntax Tree.
@ -1002,7 +1008,7 @@ class Parser
$name = $this->parseName(); $name = $this->parseName();
$interfaces = $this->parseImplementsInterfaces(); $interfaces = $this->parseImplementsInterfaces();
$directives = $this->parseDirectives(true); $directives = $this->parseDirectives(true);
$fields = $this->parseFieldDefinitions(); $fields = $this->parseFieldsDefinition();
return new ObjectTypeDefinitionNode([ return new ObjectTypeDefinitionNode([
'name' => $name, 'name' => $name,
@ -1033,13 +1039,15 @@ class Parser
* @return FieldDefinitionNode[]|NodeList * @return FieldDefinitionNode[]|NodeList
* @throws SyntaxError * @throws SyntaxError
*/ */
function parseFieldDefinitions() function parseFieldsDefinition()
{ {
return $this->many( return $this->peek(Token::BRACE_L)
? $this->many(
Token::BRACE_L, Token::BRACE_L,
[$this, 'parseFieldDefinition'], [$this, 'parseFieldDefinition'],
Token::BRACE_R Token::BRACE_R
); )
: new NodeList([]);
} }
/** /**
@ -1114,7 +1122,7 @@ class Parser
$this->expectKeyword('interface'); $this->expectKeyword('interface');
$name = $this->parseName(); $name = $this->parseName();
$directives = $this->parseDirectives(true); $directives = $this->parseDirectives(true);
$fields = $this->parseFieldDefinitions(); $fields = $this->parseFieldsDefinition();
return new InterfaceTypeDefinitionNode([ return new InterfaceTypeDefinitionNode([
'name' => $name, 'name' => $name,
@ -1136,8 +1144,7 @@ class Parser
$this->expectKeyword('union'); $this->expectKeyword('union');
$name = $this->parseName(); $name = $this->parseName();
$directives = $this->parseDirectives(true); $directives = $this->parseDirectives(true);
$this->expect(Token::EQUALS); $types = $this->parseMemberTypesDefinition();
$types = $this->parseUnionMembers();
return new UnionTypeDefinitionNode([ return new UnionTypeDefinitionNode([
'name' => $name, 'name' => $name,
@ -1149,22 +1156,23 @@ class Parser
} }
/** /**
* UnionMembers : * MemberTypes :
* - `|`? NamedType * - `|`? NamedType
* - UnionMembers | NamedType * - MemberTypes | NamedType
* *
* @return NamedTypeNode[] * @return NamedTypeNode[]
*/ */
function parseUnionMembers() function parseMemberTypesDefinition()
{ {
$types = [];
if ($this->skip(Token::EQUALS)) {
// Optional leading pipe // Optional leading pipe
$this->skip(Token::PIPE); $this->skip(Token::PIPE);
$members = [];
do { do {
$members[] = $this->parseNamedType(); $types[] = $this->parseNamedType();
} while ($this->skip(Token::PIPE)); } while ($this->skip(Token::PIPE));
return $members; }
return $types;
} }
/** /**
@ -1178,11 +1186,7 @@ class Parser
$this->expectKeyword('enum'); $this->expectKeyword('enum');
$name = $this->parseName(); $name = $this->parseName();
$directives = $this->parseDirectives(true); $directives = $this->parseDirectives(true);
$values = $this->many( $values = $this->parseEnumValuesDefinition();
Token::BRACE_L,
[$this, 'parseEnumValueDefinition'],
Token::BRACE_R
);
return new EnumTypeDefinitionNode([ return new EnumTypeDefinitionNode([
'name' => $name, 'name' => $name,
@ -1193,6 +1197,21 @@ class Parser
]); ]);
} }
/**
* @return EnumValueDefinitionNode[]|NodeList
* @throws SyntaxError
*/
function parseEnumValuesDefinition()
{
return $this->peek(Token::BRACE_L)
? $this->many(
Token::BRACE_L,
[$this, 'parseEnumValueDefinition'],
Token::BRACE_R
)
: new NodeList([]);
}
/** /**
* @return EnumValueDefinitionNode * @return EnumValueDefinitionNode
* @throws SyntaxError * @throws SyntaxError
@ -1223,11 +1242,7 @@ class Parser
$this->expectKeyword('input'); $this->expectKeyword('input');
$name = $this->parseName(); $name = $this->parseName();
$directives = $this->parseDirectives(true); $directives = $this->parseDirectives(true);
$fields = $this->many( $fields = $this->parseInputFieldsDefinition();
Token::BRACE_L,
[$this, 'parseInputValueDef'],
Token::BRACE_R
);
return new InputObjectTypeDefinitionNode([ return new InputObjectTypeDefinitionNode([
'name' => $name, 'name' => $name,
@ -1239,6 +1254,28 @@ class Parser
} }
/** /**
* @return InputValueDefinitionNode[]|NodeList
* @throws SyntaxError
*/
function parseInputFieldsDefinition() {
return $this->peek(Token::BRACE_L)
? $this->many(
Token::BRACE_L,
[$this, 'parseInputValueDef'],
Token::BRACE_R
)
: new NodeList([]);
}
/**
* TypeExtension :
* - ScalarTypeExtension
* - ObjectTypeExtension
* - InterfaceTypeExtension
* - UnionTypeExtension
* - EnumTypeExtension
* - InputObjectTypeDefinition
*
* @return TypeExtensionNode * @return TypeExtensionNode
* @throws SyntaxError * @throws SyntaxError
*/ */
@ -1248,14 +1285,45 @@ class Parser
if ($keywordToken->kind === Token::NAME) { if ($keywordToken->kind === Token::NAME) {
switch ($keywordToken->value) { switch ($keywordToken->value) {
case 'scalar':
return $this->parseScalarTypeExtension();
case 'type': case 'type':
return $this->parseObjectTypeExtension(); return $this->parseObjectTypeExtension();
case 'interface':
return $this->parseInterfaceTypeExtension();
case 'union':
return $this->parseUnionTypeExtension();
case 'enum':
return $this->parseEnumTypeExtension();
case 'input':
return $this->parseInputObjectTypeExtension();
} }
} }
throw $this->unexpected($keywordToken); throw $this->unexpected($keywordToken);
} }
/**
* @return ScalarTypeExtensionNode
* @throws SyntaxError
*/
function parseScalarTypeExtension() {
$start = $this->lexer->token;
$this->expectKeyword('extend');
$this->expectKeyword('scalar');
$name = $this->parseName();
$directives = $this->parseDirectives(true);
if (count($directives) === 0) {
throw $this->unexpected();
}
return new ScalarTypeExtensionNode([
'name' => $name,
'directives' => $directives,
'loc' => $this->loc($start)
]);
}
/** /**
* @return ObjectTypeExtensionNode * @return ObjectTypeExtensionNode
* @throws SyntaxError * @throws SyntaxError
@ -1267,9 +1335,7 @@ class Parser
$name = $this->parseName(); $name = $this->parseName();
$interfaces = $this->parseImplementsInterfaces(); $interfaces = $this->parseImplementsInterfaces();
$directives = $this->parseDirectives(true); $directives = $this->parseDirectives(true);
$fields = $this->peek(Token::BRACE_L) $fields = $this->parseFieldsDefinition();
? $this->parseFieldDefinitions()
: [];
if ( if (
count($interfaces) === 0 && count($interfaces) === 0 &&
@ -1288,6 +1354,110 @@ class Parser
]); ]);
} }
/**
* @return InterfaceTypeExtensionNode
* @throws SyntaxError
*/
function parseInterfaceTypeExtension() {
$start = $this->lexer->token;
$this->expectKeyword('extend');
$this->expectKeyword('interface');
$name = $this->parseName();
$directives = $this->parseDirectives(true);
$fields = $this->parseFieldsDefinition();
if (
count($directives) === 0 &&
count($fields) === 0
) {
throw $this->unexpected();
}
return new InterfaceTypeExtensionNode([
'name' => $name,
'directives' => $directives,
'fields' => $fields,
'loc' => $this->loc($start)
]);
}
/**
* @return UnionTypeExtensionNode
* @throws SyntaxError
*/
function parseUnionTypeExtension() {
$start = $this->lexer->token;
$this->expectKeyword('extend');
$this->expectKeyword('union');
$name = $this->parseName();
$directives = $this->parseDirectives(true);
$types = $this->parseMemberTypesDefinition();
if (
count($directives) === 0 &&
count($types) === 0
) {
throw $this->unexpected();
}
return new UnionTypeExtensionNode([
'name' => $name,
'directives' => $directives,
'types' => $types,
'loc' => $this->loc($start)
]);
}
/**
* @return EnumTypeExtensionNode
* @throws SyntaxError
*/
function parseEnumTypeExtension() {
$start = $this->lexer->token;
$this->expectKeyword('extend');
$this->expectKeyword('enum');
$name = $this->parseName();
$directives = $this->parseDirectives(true);
$values = $this->parseEnumValuesDefinition();
if (
count($directives) === 0 &&
count($values) === 0
) {
throw $this->unexpected();
}
return new EnumTypeExtensionNode([
'name' => $name,
'directives' => $directives,
'values' => $values,
'loc' => $this->loc($start)
]);
}
/**
* @return InputObjectTypeExtensionNode
* @throws SyntaxError
*/
function parseInputObjectTypeExtension() {
$start = $this->lexer->token;
$this->expectKeyword('extend');
$this->expectKeyword('input');
$name = $this->parseName();
$directives = $this->parseDirectives(true);
$fields = $this->parseInputFieldsDefinition();
if (
count($directives) === 0 &&
count($fields) === 0
) {
throw $this->unexpected();
}
return new InputObjectTypeExtensionNode([
'name' => $name,
'directives' => $directives,
'fields' => $fields,
'loc' => $this->loc($start)
]);
}
/** /**
* DirectiveDefinition : * DirectiveDefinition :
* - directive @ Name ArgumentsDefinition? on DirectiveLocations * - directive @ Name ArgumentsDefinition? on DirectiveLocations

View File

@ -4,11 +4,14 @@ namespace GraphQL\Language;
use GraphQL\Language\AST\ArgumentNode; use GraphQL\Language\AST\ArgumentNode;
use GraphQL\Language\AST\DirectiveDefinitionNode; use GraphQL\Language\AST\DirectiveDefinitionNode;
use GraphQL\Language\AST\EnumTypeDefinitionNode; use GraphQL\Language\AST\EnumTypeDefinitionNode;
use GraphQL\Language\AST\EnumTypeExtensionNode;
use GraphQL\Language\AST\EnumValueDefinitionNode; use GraphQL\Language\AST\EnumValueDefinitionNode;
use GraphQL\Language\AST\FieldDefinitionNode; use GraphQL\Language\AST\FieldDefinitionNode;
use GraphQL\Language\AST\InputObjectTypeDefinitionNode; use GraphQL\Language\AST\InputObjectTypeDefinitionNode;
use GraphQL\Language\AST\InputObjectTypeExtensionNode;
use GraphQL\Language\AST\InputValueDefinitionNode; use GraphQL\Language\AST\InputValueDefinitionNode;
use GraphQL\Language\AST\InterfaceTypeDefinitionNode; use GraphQL\Language\AST\InterfaceTypeDefinitionNode;
use GraphQL\Language\AST\InterfaceTypeExtensionNode;
use GraphQL\Language\AST\ListValueNode; use GraphQL\Language\AST\ListValueNode;
use GraphQL\Language\AST\BooleanValueNode; use GraphQL\Language\AST\BooleanValueNode;
use GraphQL\Language\AST\DirectiveNode; use GraphQL\Language\AST\DirectiveNode;
@ -32,11 +35,13 @@ use GraphQL\Language\AST\ObjectValueNode;
use GraphQL\Language\AST\OperationDefinitionNode; use GraphQL\Language\AST\OperationDefinitionNode;
use GraphQL\Language\AST\OperationTypeDefinitionNode; use GraphQL\Language\AST\OperationTypeDefinitionNode;
use GraphQL\Language\AST\ScalarTypeDefinitionNode; use GraphQL\Language\AST\ScalarTypeDefinitionNode;
use GraphQL\Language\AST\ScalarTypeExtensionNode;
use GraphQL\Language\AST\SchemaDefinitionNode; use GraphQL\Language\AST\SchemaDefinitionNode;
use GraphQL\Language\AST\SelectionSetNode; use GraphQL\Language\AST\SelectionSetNode;
use GraphQL\Language\AST\StringValueNode; use GraphQL\Language\AST\StringValueNode;
use GraphQL\Language\AST\ObjectTypeExtensionNode; use GraphQL\Language\AST\ObjectTypeExtensionNode;
use GraphQL\Language\AST\UnionTypeDefinitionNode; use GraphQL\Language\AST\UnionTypeDefinitionNode;
use GraphQL\Language\AST\UnionTypeExtensionNode;
use GraphQL\Language\AST\VariableDefinitionNode; use GraphQL\Language\AST\VariableDefinitionNode;
use GraphQL\Utils\Utils; use GraphQL\Utils\Utils;
@ -246,7 +251,9 @@ class Printer
'union', 'union',
$def->name, $def->name,
$this->join($def->directives, ' '), $this->join($def->directives, ' '),
'= ' . $this->join($def->types, ' | ') $def->types
? '= ' . $this->join($def->types, ' | ')
: ''
], ' ') ], ' ')
], "\n"); ], "\n");
}, },
@ -278,6 +285,13 @@ class Printer
], ' ') ], ' ')
], "\n"); ], "\n");
}, },
NodeKind::SCALAR_TYPE_EXTENSION => function(ScalarTypeExtensionNode $def) {
return $this->join([
'extend scalar',
$def->name,
$this->join($def->directives, ' '),
], ' ');
},
NodeKind::OBJECT_TYPE_EXTENSION => function(ObjectTypeExtensionNode $def) { NodeKind::OBJECT_TYPE_EXTENSION => function(ObjectTypeExtensionNode $def) {
return $this->join([ return $this->join([
'extend type', 'extend type',
@ -287,6 +301,40 @@ class Printer
$this->block($def->fields), $this->block($def->fields),
], ' '); ], ' ');
}, },
NodeKind::INTERFACE_TYPE_EXTENSION => function(InterfaceTypeExtensionNode $def) {
return $this->join([
'extend interface',
$def->name,
$this->join($def->directives, ' '),
$this->block($def->fields),
], ' ');
},
NodeKind::UNION_TYPE_EXTENSION => function(UnionTypeExtensionNode $def) {
return $this->join([
'extend union',
$def->name,
$this->join($def->directives, ' '),
$def->types
? '= ' . $this->join($def->types, ' | ')
: ''
], ' ');
},
NodeKind::ENUM_TYPE_EXTENSION => function(EnumTypeExtensionNode $def) {
return $this->join([
'extend enum',
$def->name,
$this->join($def->directives, ' '),
$this->block($def->values),
], ' ');
},
NodeKind::INPUT_OBJECT_TYPE_EXTENSION => function(InputObjectTypeExtensionNode $def) {
return $this->join([
'extend input',
$def->name,
$this->join($def->directives, ' '),
$this->block($def->fields),
], ' ');
},
NodeKind::DIRECTIVE_DEFINITION => function(DirectiveDefinitionNode $def) { NodeKind::DIRECTIVE_DEFINITION => function(DirectiveDefinitionNode $def) {
return $this->join([ return $this->join([
$def->description, $def->description,

View File

@ -142,7 +142,14 @@ class Visitor
NodeKind::ENUM_TYPE_DEFINITION => ['description', 'name', 'directives', 'values'], NodeKind::ENUM_TYPE_DEFINITION => ['description', 'name', 'directives', 'values'],
NodeKind::ENUM_VALUE_DEFINITION => ['description', 'name', 'directives'], NodeKind::ENUM_VALUE_DEFINITION => ['description', 'name', 'directives'],
NodeKind::INPUT_OBJECT_TYPE_DEFINITION => ['description', 'name', 'directives', 'fields'], NodeKind::INPUT_OBJECT_TYPE_DEFINITION => ['description', 'name', 'directives', 'fields'],
NodeKind::SCALAR_TYPE_EXTENSION => ['name', 'directives'],
NodeKind::OBJECT_TYPE_EXTENSION => ['name', 'interfaces', 'directives', 'fields'], NodeKind::OBJECT_TYPE_EXTENSION => ['name', 'interfaces', 'directives', 'fields'],
NodeKind::INTERFACE_TYPE_EXTENSION => ['name', 'directives', 'fields'],
NodeKind::UNION_TYPE_EXTENSION => ['name', 'directives', 'types'],
NodeKind::ENUM_TYPE_EXTENSION => ['name', 'directives', 'values'],
NodeKind::INPUT_OBJECT_TYPE_EXTENSION => ['name', 'directives', 'fields'],
NodeKind::DIRECTIVE_DEFINITION => ['description', 'name', 'arguments', 'locations'] NodeKind::DIRECTIVE_DEFINITION => ['description', 'name', 'arguments', 'locations']
]; ];

View File

@ -251,7 +251,8 @@ class ASTDefinitionBuilder
private function makeFieldDefMap($def) private function makeFieldDefMap($def)
{ {
return Utils::keyValMap( return $def->fields
? Utils::keyValMap(
$def->fields, $def->fields,
function ($field) { function ($field) {
return $field->name->value; return $field->name->value;
@ -259,7 +260,8 @@ class ASTDefinitionBuilder
function ($field) { function ($field) {
return $this->buildField($field); return $this->buildField($field);
} }
); )
: [];
} }
private function makeImplementedInterfaces(ObjectTypeDefinitionNode $def) private function makeImplementedInterfaces(ObjectTypeDefinitionNode $def)
@ -313,8 +315,8 @@ class ASTDefinitionBuilder
return new EnumType([ return new EnumType([
'name' => $def->name->value, 'name' => $def->name->value,
'description' => $this->getDescription($def), 'description' => $this->getDescription($def),
'astNode' => $def, 'values' => $def->values
'values' => Utils::keyValMap( ? Utils::keyValMap(
$def->values, $def->values,
function ($enumValue) { function ($enumValue) {
return $enumValue->name->value; return $enumValue->name->value;
@ -327,6 +329,8 @@ class ASTDefinitionBuilder
]; ];
} }
) )
: [],
'astNode' => $def,
]); ]);
} }
@ -335,10 +339,12 @@ class ASTDefinitionBuilder
return new UnionType([ return new UnionType([
'name' => $def->name->value, 'name' => $def->name->value,
'description' => $this->getDescription($def), 'description' => $this->getDescription($def),
'types' => Utils::map($def->types, function ($typeNode) { 'types' => $def->types
? Utils::map($def->types, function ($typeNode) {
return $this->buildObjectType($typeNode); return $this->buildObjectType($typeNode);
}), }):
'astNode' => $def [],
'astNode' => $def,
]); ]);
} }
@ -360,7 +366,9 @@ class ASTDefinitionBuilder
'name' => $def->name->value, 'name' => $def->name->value,
'description' => $this->getDescription($def), 'description' => $this->getDescription($def),
'fields' => function () use ($def) { 'fields' => function () use ($def) {
return $this->makeInputValues($def->fields); return $def->fields
? $this->makeInputValues($def->fields)
: [];
}, },
'astNode' => $def, 'astNode' => $def,
]); ]);

View File

@ -67,20 +67,38 @@ class KnownDirectives extends AbstractValidationRule
case 'subscription': return DirectiveLocation::SUBSCRIPTION; case 'subscription': return DirectiveLocation::SUBSCRIPTION;
} }
break; break;
case NodeKind::FIELD: return DirectiveLocation::FIELD; case NodeKind::FIELD:
case NodeKind::FRAGMENT_SPREAD: return DirectiveLocation::FRAGMENT_SPREAD; return DirectiveLocation::FIELD;
case NodeKind::INLINE_FRAGMENT: return DirectiveLocation::INLINE_FRAGMENT; case NodeKind::FRAGMENT_SPREAD:
case NodeKind::FRAGMENT_DEFINITION: return DirectiveLocation::FRAGMENT_DEFINITION; return DirectiveLocation::FRAGMENT_SPREAD;
case NodeKind::SCHEMA_DEFINITION: return DirectiveLocation::SCHEMA; case NodeKind::INLINE_FRAGMENT:
case NodeKind::SCALAR_TYPE_DEFINITION: return DirectiveLocation::SCALAR; return DirectiveLocation::INLINE_FRAGMENT;
case NodeKind::FRAGMENT_DEFINITION:
return DirectiveLocation::FRAGMENT_DEFINITION;
case NodeKind::SCHEMA_DEFINITION:
return DirectiveLocation::SCHEMA;
case NodeKind::SCALAR_TYPE_DEFINITION:
case NodeKind::SCALAR_TYPE_EXTENSION:
return DirectiveLocation::SCALAR;
case NodeKind::OBJECT_TYPE_DEFINITION: case NodeKind::OBJECT_TYPE_DEFINITION:
case NodeKind::OBJECT_TYPE_EXTENSION: return DirectiveLocation::OBJECT; case NodeKind::OBJECT_TYPE_EXTENSION:
case NodeKind::FIELD_DEFINITION: return DirectiveLocation::FIELD_DEFINITION; return DirectiveLocation::OBJECT;
case NodeKind::INTERFACE_TYPE_DEFINITION: return DirectiveLocation::IFACE; case NodeKind::FIELD_DEFINITION:
case NodeKind::UNION_TYPE_DEFINITION: return DirectiveLocation::UNION; return DirectiveLocation::FIELD_DEFINITION;
case NodeKind::ENUM_TYPE_DEFINITION: return DirectiveLocation::ENUM; case NodeKind::INTERFACE_TYPE_DEFINITION:
case NodeKind::ENUM_VALUE_DEFINITION: return DirectiveLocation::ENUM_VALUE; case NodeKind::INTERFACE_TYPE_EXTENSION:
case NodeKind::INPUT_OBJECT_TYPE_DEFINITION: return DirectiveLocation::INPUT_OBJECT; return DirectiveLocation::IFACE;
case NodeKind::UNION_TYPE_DEFINITION:
case NodeKind::UNION_TYPE_EXTENSION:
return DirectiveLocation::UNION;
case NodeKind::ENUM_TYPE_DEFINITION:
case NodeKind::ENUM_TYPE_EXTENSION:
return DirectiveLocation::ENUM;
case NodeKind::ENUM_VALUE_DEFINITION:
return DirectiveLocation::ENUM_VALUE;
case NodeKind::INPUT_OBJECT_TYPE_DEFINITION:
case NodeKind::INPUT_OBJECT_TYPE_EXTENSION:
return DirectiveLocation::INPUT_OBJECT;
case NodeKind::INPUT_VALUE_DEFINITION: case NodeKind::INPUT_VALUE_DEFINITION:
$parentNode = $ancestors[count($ancestors) - 3]; $parentNode = $ancestors[count($ancestors) - 3];
return $parentNode instanceof InputObjectTypeDefinitionNode return $parentNode instanceof InputObjectTypeDefinitionNode

View File

@ -74,6 +74,14 @@ type AnnotatedObject @onObject(arg: "value") {
annotatedField(arg: Type = "default" @onArg): Type @onField annotatedField(arg: Type = "default" @onArg): Type @onField
} }
type UndefinedType
extend type Foo {
seven(argument: [String]): Type
}
extend type Foo @onType
interface Bar { interface Bar {
one: Type one: Type
four(argument: String = "string"): String four(argument: String = "string"): String
@ -83,16 +91,32 @@ interface AnnotatedInterface @onInterface {
annotatedField(arg: Type @onArg): Type @onField annotatedField(arg: Type @onArg): Type @onField
} }
interface UndefinedInterface
extend interface Bar {
two(argument: InputType!): Type
}
extend interface Bar @onInterface
union Feed = Story | Article | Advert union Feed = Story | Article | Advert
union AnnotatedUnion @onUnion = A | B union AnnotatedUnion @onUnion = A | B
union AnnotatedUnionTwo @onUnion = A | B union AnnotatedUnionTwo @onUnion = A | B
union UndefinedUnion
extend union Feed = Photo | Video
extend union Feed @onUnion
scalar CustomScalar scalar CustomScalar
scalar AnnotatedScalar @onScalar scalar AnnotatedScalar @onScalar
extend scalar CustomScalar @onScalar
enum Site { enum Site {
DESKTOP DESKTOP
MOBILE MOBILE
@ -103,20 +127,30 @@ enum AnnotatedEnum @onEnum {
OTHER_VALUE OTHER_VALUE
} }
enum UndefinedEnum
extend enum Site {
VR
}
extend enum Site @onEnum
input InputType { input InputType {
key: String! key: String!
answer: Int = 42 answer: Int = 42
} }
input AnnotatedInput @onInputObjectType { input AnnotatedInput @onInputObject {
annotatedField: Type @onField annotatedField: Type @onField
} }
extend type Foo { input UndefinedInput
seven(argument: [String]): Type
extend input InputType {
other: Float = 1.23e4
} }
extend type Foo @onType extend input InputType @onInputObject
directive @skip(if: Boolean!) on FIELD | FRAGMENT_SPREAD | INLINE_FRAGMENT directive @skip(if: Boolean!) on FIELD | FRAGMENT_SPREAD | INLINE_FRAGMENT

View File

@ -26,6 +26,14 @@ type AnnotatedObject @onObject(arg: "value") {
annotatedField(arg: Type = "default" @onArg): Type @onField annotatedField(arg: Type = "default" @onArg): Type @onField
} }
type UndefinedType
extend type Foo {
seven(argument: [String]): Type
}
extend type Foo @onType
interface Bar { interface Bar {
one: Type one: Type
four(argument: String = "string"): String four(argument: String = "string"): String
@ -35,16 +43,32 @@ interface AnnotatedInterface @onInterface {
annotatedField(arg: Type @onArg): Type @onField annotatedField(arg: Type @onArg): Type @onField
} }
interface UndefinedInterface
extend interface Bar {
two(argument: InputType!): Type
}
extend interface Bar @onInterface
union Feed = Story | Article | Advert union Feed = Story | Article | Advert
union AnnotatedUnion @onUnion = A | B union AnnotatedUnion @onUnion = A | B
union AnnotatedUnionTwo @onUnion = | A | B union AnnotatedUnionTwo @onUnion = | A | B
union UndefinedUnion
extend union Feed = Photo | Video
extend union Feed @onUnion
scalar CustomScalar scalar CustomScalar
scalar AnnotatedScalar @onScalar scalar AnnotatedScalar @onScalar
extend scalar CustomScalar @onScalar
enum Site { enum Site {
DESKTOP DESKTOP
MOBILE MOBILE
@ -55,20 +79,30 @@ enum AnnotatedEnum @onEnum {
OTHER_VALUE OTHER_VALUE
} }
enum UndefinedEnum
extend enum Site {
VR
}
extend enum Site @onEnum
input InputType { input InputType {
key: String! key: String!
answer: Int = 42 answer: Int = 42
} }
input AnnotatedInput @onInputObjectType { input AnnotatedInput @onInputObject {
annotatedField: Type @onField annotatedField: Type @onField
} }
extend type Foo { input UndefinedInput
seven(argument: [String]): Type
extend input InputType {
other: Float = 1.23e4
} }
extend type Foo @onType extend input InputType @onInputObject
directive @skip(if: Boolean!) on FIELD | FRAGMENT_SPREAD | INLINE_FRAGMENT directive @skip(if: Boolean!) on FIELD | FRAGMENT_SPREAD | INLINE_FRAGMENT

View File

@ -140,20 +140,30 @@ class KnownDirectivesTest extends TestCase
scalar MyScalar @onScalar scalar MyScalar @onScalar
extend scalar MyScalar @onScalar
interface MyInterface @onInterface { interface MyInterface @onInterface {
myField(myArg: Int @onArgumentDefinition): String @onFieldDefinition myField(myArg: Int @onArgumentDefinition): String @onFieldDefinition
} }
extend interface MyInterface @onInterface
union MyUnion @onUnion = MyObj | Other union MyUnion @onUnion = MyObj | Other
extend union MyUnion @onUnion
enum MyEnum @onEnum { enum MyEnum @onEnum {
MY_VALUE @onEnumValue MY_VALUE @onEnumValue
} }
extend enum MyEnum @onEnum
input MyInput @onInputObject { input MyInput @onInputObject {
myField: Int @onInputFieldDefinition myField: Int @onInputFieldDefinition
} }
extend input MyInput @onInputObject
schema @onSchema { schema @onSchema {
query: MyQuery query: MyQuery
} }