diff --git a/src/Language/AST/EnumTypeDefinitionNode.php b/src/Language/AST/EnumTypeDefinitionNode.php index 71ca508..e9be727 100644 --- a/src/Language/AST/EnumTypeDefinitionNode.php +++ b/src/Language/AST/EnumTypeDefinitionNode.php @@ -19,7 +19,7 @@ class EnumTypeDefinitionNode extends Node implements TypeDefinitionNode public $directives; /** - * @var EnumValueDefinitionNode[] + * @var EnumValueDefinitionNode[]|null */ public $values; diff --git a/src/Language/AST/EnumTypeExtensionNode.php b/src/Language/AST/EnumTypeExtensionNode.php new file mode 100644 index 0000000..5e2417d --- /dev/null +++ b/src/Language/AST/EnumTypeExtensionNode.php @@ -0,0 +1,25 @@ +InputObjectTypeDefinitionNode::class, // Type Extensions + NodeKind::SCALAR_TYPE_EXTENSION => ScalarTypeExtensionNode::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 NodeKind::DIRECTIVE_DEFINITION => DirectiveDefinitionNode::class diff --git a/src/Language/AST/ObjectTypeDefinitionNode.php b/src/Language/AST/ObjectTypeDefinitionNode.php index addf20a..b2c6b1b 100644 --- a/src/Language/AST/ObjectTypeDefinitionNode.php +++ b/src/Language/AST/ObjectTypeDefinitionNode.php @@ -19,12 +19,12 @@ class ObjectTypeDefinitionNode extends Node implements TypeDefinitionNode public $interfaces = []; /** - * @var DirectiveNode[] + * @var DirectiveNode[]|null */ public $directives; /** - * @var FieldDefinitionNode[] + * @var FieldDefinitionNode[]|null */ public $fields; diff --git a/src/Language/AST/ScalarTypeExtensionNode.php b/src/Language/AST/ScalarTypeExtensionNode.php new file mode 100644 index 0000000..1fc2d82 --- /dev/null +++ b/src/Language/AST/ScalarTypeExtensionNode.php @@ -0,0 +1,20 @@ +parseName(); $interfaces = $this->parseImplementsInterfaces(); $directives = $this->parseDirectives(true); - $fields = $this->parseFieldDefinitions(); + $fields = $this->parseFieldsDefinition(); return new ObjectTypeDefinitionNode([ 'name' => $name, @@ -1033,13 +1039,15 @@ class Parser * @return FieldDefinitionNode[]|NodeList * @throws SyntaxError */ - function parseFieldDefinitions() + function parseFieldsDefinition() { - return $this->many( - Token::BRACE_L, - [$this, 'parseFieldDefinition'], - Token::BRACE_R - ); + return $this->peek(Token::BRACE_L) + ? $this->many( + Token::BRACE_L, + [$this, 'parseFieldDefinition'], + Token::BRACE_R + ) + : new NodeList([]); } /** @@ -1114,7 +1122,7 @@ class Parser $this->expectKeyword('interface'); $name = $this->parseName(); $directives = $this->parseDirectives(true); - $fields = $this->parseFieldDefinitions(); + $fields = $this->parseFieldsDefinition(); return new InterfaceTypeDefinitionNode([ 'name' => $name, @@ -1136,8 +1144,7 @@ class Parser $this->expectKeyword('union'); $name = $this->parseName(); $directives = $this->parseDirectives(true); - $this->expect(Token::EQUALS); - $types = $this->parseUnionMembers(); + $types = $this->parseMemberTypesDefinition(); return new UnionTypeDefinitionNode([ 'name' => $name, @@ -1149,22 +1156,23 @@ class Parser } /** - * UnionMembers : + * MemberTypes : * - `|`? NamedType - * - UnionMembers | NamedType + * - MemberTypes | NamedType * * @return NamedTypeNode[] */ - function parseUnionMembers() + function parseMemberTypesDefinition() { - // Optional leading pipe - $this->skip(Token::PIPE); - $members = []; - - do { - $members[] = $this->parseNamedType(); - } while ($this->skip(Token::PIPE)); - return $members; + $types = []; + if ($this->skip(Token::EQUALS)) { + // Optional leading pipe + $this->skip(Token::PIPE); + do { + $types[] = $this->parseNamedType(); + } while ($this->skip(Token::PIPE)); + } + return $types; } /** @@ -1178,11 +1186,7 @@ class Parser $this->expectKeyword('enum'); $name = $this->parseName(); $directives = $this->parseDirectives(true); - $values = $this->many( - Token::BRACE_L, - [$this, 'parseEnumValueDefinition'], - Token::BRACE_R - ); + $values = $this->parseEnumValuesDefinition(); return new EnumTypeDefinitionNode([ '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 * @throws SyntaxError @@ -1223,11 +1242,7 @@ class Parser $this->expectKeyword('input'); $name = $this->parseName(); $directives = $this->parseDirectives(true); - $fields = $this->many( - Token::BRACE_L, - [$this, 'parseInputValueDef'], - Token::BRACE_R - ); + $fields = $this->parseInputFieldsDefinition(); return new InputObjectTypeDefinitionNode([ '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 * @throws SyntaxError */ @@ -1248,14 +1285,45 @@ class Parser if ($keywordToken->kind === Token::NAME) { switch ($keywordToken->value) { + case 'scalar': + return $this->parseScalarTypeExtension(); case 'type': 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); } + /** + * @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 * @throws SyntaxError @@ -1267,9 +1335,7 @@ class Parser $name = $this->parseName(); $interfaces = $this->parseImplementsInterfaces(); $directives = $this->parseDirectives(true); - $fields = $this->peek(Token::BRACE_L) - ? $this->parseFieldDefinitions() - : []; + $fields = $this->parseFieldsDefinition(); if ( 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 : * - directive @ Name ArgumentsDefinition? on DirectiveLocations diff --git a/src/Language/Printer.php b/src/Language/Printer.php index b835b4d..7c123a3 100644 --- a/src/Language/Printer.php +++ b/src/Language/Printer.php @@ -4,11 +4,14 @@ namespace GraphQL\Language; use GraphQL\Language\AST\ArgumentNode; use GraphQL\Language\AST\DirectiveDefinitionNode; use GraphQL\Language\AST\EnumTypeDefinitionNode; +use GraphQL\Language\AST\EnumTypeExtensionNode; use GraphQL\Language\AST\EnumValueDefinitionNode; use GraphQL\Language\AST\FieldDefinitionNode; use GraphQL\Language\AST\InputObjectTypeDefinitionNode; +use GraphQL\Language\AST\InputObjectTypeExtensionNode; use GraphQL\Language\AST\InputValueDefinitionNode; use GraphQL\Language\AST\InterfaceTypeDefinitionNode; +use GraphQL\Language\AST\InterfaceTypeExtensionNode; use GraphQL\Language\AST\ListValueNode; use GraphQL\Language\AST\BooleanValueNode; use GraphQL\Language\AST\DirectiveNode; @@ -32,11 +35,13 @@ use GraphQL\Language\AST\ObjectValueNode; use GraphQL\Language\AST\OperationDefinitionNode; use GraphQL\Language\AST\OperationTypeDefinitionNode; use GraphQL\Language\AST\ScalarTypeDefinitionNode; +use GraphQL\Language\AST\ScalarTypeExtensionNode; use GraphQL\Language\AST\SchemaDefinitionNode; use GraphQL\Language\AST\SelectionSetNode; use GraphQL\Language\AST\StringValueNode; use GraphQL\Language\AST\ObjectTypeExtensionNode; use GraphQL\Language\AST\UnionTypeDefinitionNode; +use GraphQL\Language\AST\UnionTypeExtensionNode; use GraphQL\Language\AST\VariableDefinitionNode; use GraphQL\Utils\Utils; @@ -246,7 +251,9 @@ class Printer 'union', $def->name, $this->join($def->directives, ' '), - '= ' . $this->join($def->types, ' | ') + $def->types + ? '= ' . $this->join($def->types, ' | ') + : '' ], ' ') ], "\n"); }, @@ -278,6 +285,13 @@ class Printer ], ' ') ], "\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) { return $this->join([ 'extend type', @@ -287,6 +301,40 @@ class Printer $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) { return $this->join([ $def->description, diff --git a/src/Language/Visitor.php b/src/Language/Visitor.php index fb149cd..fc0a1e7 100644 --- a/src/Language/Visitor.php +++ b/src/Language/Visitor.php @@ -142,7 +142,14 @@ class Visitor NodeKind::ENUM_TYPE_DEFINITION => ['description', 'name', 'directives', 'values'], NodeKind::ENUM_VALUE_DEFINITION => ['description', 'name', 'directives'], NodeKind::INPUT_OBJECT_TYPE_DEFINITION => ['description', 'name', 'directives', 'fields'], - NodeKind::OBJECT_TYPE_EXTENSION => [ 'name', 'interfaces', 'directives', 'fields' ], + + NodeKind::SCALAR_TYPE_EXTENSION => ['name', 'directives'], + 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'] ]; diff --git a/src/Utils/ASTDefinitionBuilder.php b/src/Utils/ASTDefinitionBuilder.php index d6d7f7a..d38310b 100644 --- a/src/Utils/ASTDefinitionBuilder.php +++ b/src/Utils/ASTDefinitionBuilder.php @@ -251,15 +251,17 @@ class ASTDefinitionBuilder private function makeFieldDefMap($def) { - return Utils::keyValMap( - $def->fields, - function ($field) { - return $field->name->value; - }, - function ($field) { - return $this->buildField($field); - } - ); + return $def->fields + ? Utils::keyValMap( + $def->fields, + function ($field) { + return $field->name->value; + }, + function ($field) { + return $this->buildField($field); + } + ) + : []; } private function makeImplementedInterfaces(ObjectTypeDefinitionNode $def) @@ -313,20 +315,22 @@ class ASTDefinitionBuilder return new EnumType([ 'name' => $def->name->value, 'description' => $this->getDescription($def), + 'values' => $def->values + ? Utils::keyValMap( + $def->values, + function ($enumValue) { + return $enumValue->name->value; + }, + function ($enumValue) { + return [ + 'description' => $this->getDescription($enumValue), + 'deprecationReason' => $this->getDeprecationReason($enumValue), + 'astNode' => $enumValue + ]; + } + ) + : [], 'astNode' => $def, - 'values' => Utils::keyValMap( - $def->values, - function ($enumValue) { - return $enumValue->name->value; - }, - function ($enumValue) { - return [ - 'description' => $this->getDescription($enumValue), - 'deprecationReason' => $this->getDeprecationReason($enumValue), - 'astNode' => $enumValue - ]; - } - ) ]); } @@ -335,10 +339,12 @@ class ASTDefinitionBuilder return new UnionType([ 'name' => $def->name->value, 'description' => $this->getDescription($def), - 'types' => Utils::map($def->types, function ($typeNode) { - return $this->buildObjectType($typeNode); - }), - 'astNode' => $def + 'types' => $def->types + ? Utils::map($def->types, function ($typeNode) { + return $this->buildObjectType($typeNode); + }): + [], + 'astNode' => $def, ]); } @@ -360,7 +366,9 @@ class ASTDefinitionBuilder 'name' => $def->name->value, 'description' => $this->getDescription($def), 'fields' => function () use ($def) { - return $this->makeInputValues($def->fields); + return $def->fields + ? $this->makeInputValues($def->fields) + : []; }, 'astNode' => $def, ]); diff --git a/src/Validator/Rules/KnownDirectives.php b/src/Validator/Rules/KnownDirectives.php index 9d48aa5..4ec3a01 100644 --- a/src/Validator/Rules/KnownDirectives.php +++ b/src/Validator/Rules/KnownDirectives.php @@ -67,20 +67,38 @@ class KnownDirectives extends AbstractValidationRule case 'subscription': return DirectiveLocation::SUBSCRIPTION; } break; - case NodeKind::FIELD: return DirectiveLocation::FIELD; - case NodeKind::FRAGMENT_SPREAD: return DirectiveLocation::FRAGMENT_SPREAD; - case NodeKind::INLINE_FRAGMENT: return DirectiveLocation::INLINE_FRAGMENT; - case NodeKind::FRAGMENT_DEFINITION: return DirectiveLocation::FRAGMENT_DEFINITION; - case NodeKind::SCHEMA_DEFINITION: return DirectiveLocation::SCHEMA; - case NodeKind::SCALAR_TYPE_DEFINITION: return DirectiveLocation::SCALAR; + case NodeKind::FIELD: + return DirectiveLocation::FIELD; + case NodeKind::FRAGMENT_SPREAD: + return DirectiveLocation::FRAGMENT_SPREAD; + case NodeKind::INLINE_FRAGMENT: + 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_EXTENSION: return DirectiveLocation::OBJECT; - case NodeKind::FIELD_DEFINITION: return DirectiveLocation::FIELD_DEFINITION; - case NodeKind::INTERFACE_TYPE_DEFINITION: return DirectiveLocation::IFACE; - case NodeKind::UNION_TYPE_DEFINITION: return DirectiveLocation::UNION; - case NodeKind::ENUM_TYPE_DEFINITION: return DirectiveLocation::ENUM; - case NodeKind::ENUM_VALUE_DEFINITION: return DirectiveLocation::ENUM_VALUE; - case NodeKind::INPUT_OBJECT_TYPE_DEFINITION: return DirectiveLocation::INPUT_OBJECT; + case NodeKind::OBJECT_TYPE_EXTENSION: + return DirectiveLocation::OBJECT; + case NodeKind::FIELD_DEFINITION: + return DirectiveLocation::FIELD_DEFINITION; + case NodeKind::INTERFACE_TYPE_DEFINITION: + case NodeKind::INTERFACE_TYPE_EXTENSION: + 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: $parentNode = $ancestors[count($ancestors) - 3]; return $parentNode instanceof InputObjectTypeDefinitionNode diff --git a/tests/Language/SchemaPrinterTest.php b/tests/Language/SchemaPrinterTest.php index 3141f06..1e1b897 100644 --- a/tests/Language/SchemaPrinterTest.php +++ b/tests/Language/SchemaPrinterTest.php @@ -74,6 +74,14 @@ type AnnotatedObject @onObject(arg: "value") { annotatedField(arg: Type = "default" @onArg): Type @onField } +type UndefinedType + +extend type Foo { + seven(argument: [String]): Type +} + +extend type Foo @onType + interface Bar { one: Type four(argument: String = "string"): String @@ -83,16 +91,32 @@ interface AnnotatedInterface @onInterface { 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 AnnotatedUnion @onUnion = A | B union AnnotatedUnionTwo @onUnion = A | B +union UndefinedUnion + +extend union Feed = Photo | Video + +extend union Feed @onUnion + scalar CustomScalar scalar AnnotatedScalar @onScalar +extend scalar CustomScalar @onScalar + enum Site { DESKTOP MOBILE @@ -103,20 +127,30 @@ enum AnnotatedEnum @onEnum { OTHER_VALUE } +enum UndefinedEnum + +extend enum Site { + VR +} + +extend enum Site @onEnum + input InputType { key: String! answer: Int = 42 } -input AnnotatedInput @onInputObjectType { +input AnnotatedInput @onInputObject { annotatedField: Type @onField } -extend type Foo { - seven(argument: [String]): Type +input UndefinedInput + +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 diff --git a/tests/Language/schema-kitchen-sink.graphql b/tests/Language/schema-kitchen-sink.graphql index 016707d..ae1a3e5 100644 --- a/tests/Language/schema-kitchen-sink.graphql +++ b/tests/Language/schema-kitchen-sink.graphql @@ -13,20 +13,28 @@ This is a description of the `Foo` type. """ type Foo implements Bar { - one: Type - two(argument: InputType!): Type - three(argument: InputType, other: String): Int - four(argument: String = "string"): String - five(argument: [String] = ["string", "string"]): String - six(argument: InputType = {key: "value"}): Type - seven(argument: Int = null): Type +one: Type +two(argument: InputType!): Type +three(argument: InputType, other: String): Int +four(argument: String = "string"): String +five(argument: [String] = ["string", "string"]): String +six(argument: InputType = {key: "value"}): Type +seven(argument: Int = null): Type } type AnnotatedObject @onObject(arg: "value") { annotatedField(arg: Type = "default" @onArg): Type @onField } -interface Bar { +type UndefinedType + + extend type Foo { + seven(argument: [String]): Type +} + +extend type Foo @onType + + interface Bar { one: Type four(argument: String = "string"): String } @@ -35,49 +43,75 @@ interface AnnotatedInterface @onInterface { 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 AnnotatedUnion @onUnion = A | B union AnnotatedUnionTwo @onUnion = | A | B +union UndefinedUnion + +extend union Feed = Photo | Video + +extend union Feed @onUnion + scalar CustomScalar scalar AnnotatedScalar @onScalar +extend scalar CustomScalar @onScalar + enum Site { - DESKTOP - MOBILE +DESKTOP +MOBILE } enum AnnotatedEnum @onEnum { - ANNOTATED_VALUE @onEnumValue - OTHER_VALUE +ANNOTATED_VALUE @onEnumValue +OTHER_VALUE } +enum UndefinedEnum + +extend enum Site { +VR +} + +extend enum Site @onEnum + input InputType { - key: String! - answer: Int = 42 +key: String! +answer: Int = 42 } -input AnnotatedInput @onInputObjectType { - annotatedField: Type @onField +input AnnotatedInput @onInputObject { +annotatedField: Type @onField } -extend type Foo { - seven(argument: [String]): Type +input UndefinedInput + +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 @include(if: Boolean!) - on FIELD - | FRAGMENT_SPREAD - | INLINE_FRAGMENT +on FIELD +| FRAGMENT_SPREAD +| INLINE_FRAGMENT directive @include2(if: Boolean!) on - | FIELD - | FRAGMENT_SPREAD - | INLINE_FRAGMENT +| FIELD +| FRAGMENT_SPREAD +| INLINE_FRAGMENT diff --git a/tests/Validator/KnownDirectivesTest.php b/tests/Validator/KnownDirectivesTest.php index 3e7998e..9374b73 100644 --- a/tests/Validator/KnownDirectivesTest.php +++ b/tests/Validator/KnownDirectivesTest.php @@ -139,20 +139,30 @@ class KnownDirectivesTest extends TestCase extend type MyObj @onObject scalar MyScalar @onScalar + + extend scalar MyScalar @onScalar interface MyInterface @onInterface { myField(myArg: Int @onArgumentDefinition): String @onFieldDefinition } + + extend interface MyInterface @onInterface union MyUnion @onUnion = MyObj | Other + + extend union MyUnion @onUnion enum MyEnum @onEnum { MY_VALUE @onEnumValue } + + extend enum MyEnum @onEnum input MyInput @onInputObject { myField: Int @onInputFieldDefinition } + + extend input MyInput @onInputObject schema @onSchema { query: MyQuery