From e87460880c49393fa8bb823409a0e65233f6d873 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aur=C3=A9lien=20David?= Date: Wed, 12 Jun 2019 11:59:42 +0200 Subject: [PATCH] QueryPlan can now be used on interfaces not only objects. It's often the case to use interfaces in queries: interface Pet { name: String! } Query { pets: [Pet] } --- src/Type/Definition/QueryPlan.php | 4 +- tests/Type/QueryPlanTest.php | 113 ++++++++++++++++++++++++++++++ 2 files changed, 116 insertions(+), 1 deletion(-) diff --git a/src/Type/Definition/QueryPlan.php b/src/Type/Definition/QueryPlan.php index 235066d..641686d 100644 --- a/src/Type/Definition/QueryPlan.php +++ b/src/Type/Definition/QueryPlan.php @@ -140,9 +140,11 @@ class QueryPlan /** * @return mixed[] * + * $parentType InterfaceType|ObjectType. + * * @throws Error */ - private function analyzeSelectionSet(SelectionSetNode $selectionSet, ObjectType $parentType) : array + private function analyzeSelectionSet(SelectionSetNode $selectionSet, Type $parentType) : array { $fields = []; foreach ($selectionSet->selections as $selectionNode) { diff --git a/tests/Type/QueryPlanTest.php b/tests/Type/QueryPlanTest.php index 50ec6ae..73f8ae7 100644 --- a/tests/Type/QueryPlanTest.php +++ b/tests/Type/QueryPlanTest.php @@ -5,6 +5,8 @@ declare(strict_types=1); namespace GraphQL\Tests\Type; use GraphQL\GraphQL; +use GraphQL\Tests\Executor\TestClasses\Dog; +use GraphQL\Type\Definition\InterfaceType; use GraphQL\Type\Definition\ObjectType; use GraphQL\Type\Definition\QueryPlan; use GraphQL\Type\Definition\ResolveInfo; @@ -295,6 +297,117 @@ final class QueryPlanTest extends TestCase self::assertFalse($queryPlan->hasType('Test')); } + public function testQueryPlanOnInterface() : void + { + $petType = new InterfaceType([ + 'name' => 'Pet', + 'fields' => static function () { + return [ + 'name' => ['type' => Type::string()], + ]; + }, + ]); + + $dogType = new ObjectType([ + 'name' => 'Dog', + 'interfaces' => [$petType], + 'isTypeOf' => static function ($obj) { + return $obj instanceof Dog; + }, + 'fields' => static function () { + return [ + 'name' => ['type' => Type::string()], + 'woofs' => ['type' => Type::boolean()], + ]; + }, + ]); + + $query = 'query Test { + pets { + name + ... on Dog { + woofs + } + } + }'; + + $expectedQueryPlan = [ + 'woofs' => [ + 'type' => Type::boolean(), + 'fields' => [], + 'args' => [], + ], + 'name' => [ + 'type' => Type::string(), + 'args' => [], + 'fields' => [], + ], + ]; + + $expectedReferencedTypes = [ + 'Dog', + 'Pet', + ]; + + $expectedReferencedFields = [ + 'woofs', + 'name', + ]; + + /** @var QueryPlan $queryPlan */ + $queryPlan = null; + $hasCalled = false; + + $petsQuery = new ObjectType([ + 'name' => 'Query', + 'fields' => [ + 'pets' => [ + 'type' => Type::listOf($petType), + 'resolve' => static function ( + $value, + $args, + $context, + ResolveInfo $info + ) use ( + &$hasCalled, + &$queryPlan +) { + $hasCalled = true; + $queryPlan = $info->lookAhead(); + + return []; + }, + ], + ], + ]); + + $schema = new Schema([ + 'query' => $petsQuery, + 'types' => [$dogType], + 'typeLoader' => static function ($name) use ($dogType, $petType) { + switch ($name) { + case 'Dog': + return $dogType; + case 'Pet': + return $petType; + } + }, + ]); + $result = GraphQL::executeQuery($schema, $query)->toArray(); + + self::assertTrue($hasCalled); + self::assertEquals($expectedQueryPlan, $queryPlan->queryPlan()); + self::assertEquals($expectedReferencedTypes, $queryPlan->getReferencedTypes()); + self::assertEquals($expectedReferencedFields, $queryPlan->getReferencedFields()); + self::assertEquals(['woofs'], $queryPlan->subFields('Dog')); + + self::assertTrue($queryPlan->hasField('name')); + self::assertFalse($queryPlan->hasField('test')); + + self::assertTrue($queryPlan->hasType('Dog')); + self::assertFalse($queryPlan->hasType('Test')); + } + public function testMergedFragmentsQueryPlan() : void { $image = new ObjectType([