From 91eda4470842563b32fcf85fccc1e3136722eda6 Mon Sep 17 00:00:00 2001 From: Yury Antonau Date: Thu, 12 Mar 2020 22:50:23 +0300 Subject: [PATCH] Working at any nesting level, impoved tests --- src/Type/Definition/QueryPlan.php | 21 ++-- tests/Type/QueryPlanTest.php | 196 ++++++++++++------------------ 2 files changed, 89 insertions(+), 128 deletions(-) diff --git a/src/Type/Definition/QueryPlan.php b/src/Type/Definition/QueryPlan.php index 03788232b..8a4f55921 100644 --- a/src/Type/Definition/QueryPlan.php +++ b/src/Type/Definition/QueryPlan.php @@ -161,18 +161,20 @@ private function analyzeQueryPlan(ObjectType $parentType, iterable $fieldNodes) * * @throws Error */ - private function analyzeSelectionSet(SelectionSetNode $selectionSet, Type $parentType, array &$implementors = []) : array + private function analyzeSelectionSet(SelectionSetNode $selectionSet, Type $parentType, array &$implementors) : array { - $fields = []; + $fields = []; + $implementors = []; foreach ($selectionSet->selections as $selectionNode) { if ($selectionNode instanceof FieldNode) { $fieldName = $selectionNode->name->value; $type = $parentType->getField($fieldName); $selectionType = $type->getType(); - $subfields = []; + $subfields = []; + $subImplementors = []; if ($selectionNode->selectionSet) { - $subfields = $this->analyzeSubFields($selectionType, $selectionNode->selectionSet); + $subfields = $this->analyzeSubFields($selectionType, $selectionNode->selectionSet, $subImplementors); } $fields[$fieldName] = [ @@ -180,6 +182,9 @@ private function analyzeSelectionSet(SelectionSetNode $selectionSet, Type $paren 'fields' => $subfields ?? [], 'args' => Values::getArgumentValues($type, $selectionNode, $this->variableValues), ]; + if ($this->groupImplementorFields && $subImplementors) { + $fields[$fieldName]['implementors'] = $subImplementors; + } } elseif ($selectionNode instanceof FragmentSpreadNode) { $spreadName = $selectionNode->name->value; if (isset($this->fragments[$spreadName])) { @@ -199,17 +204,19 @@ private function analyzeSelectionSet(SelectionSetNode $selectionSet, Type $paren } /** + * @param mixed[] $implementors + * * @return mixed[] */ - private function analyzeSubFields(Type $type, SelectionSetNode $selectionSet) : array + private function analyzeSubFields(Type $type, SelectionSetNode $selectionSet, array &$implementors = []) : array { if ($type instanceof WrappingType) { $type = $type->getWrappedType(); } $subfields = []; - if ($type instanceof ObjectType) { - $subfields = $this->analyzeSelectionSet($selectionSet, $type); + if ($type instanceof ObjectType || $type instanceof AbstractType) { + $subfields = $this->analyzeSelectionSet($selectionSet, $type, $implementors); $this->types[$type->name] = array_unique(array_merge( array_key_exists($type->name, $this->types) ? $this->types[$type->name] : [], array_keys($subfields) diff --git a/tests/Type/QueryPlanTest.php b/tests/Type/QueryPlanTest.php index 0e0e5abfa..adb9f0c36 100644 --- a/tests/Type/QueryPlanTest.php +++ b/tests/Type/QueryPlanTest.php @@ -700,7 +700,7 @@ public function testMergedFragmentsQueryPlan() : void self::assertFalse($queryPlan->hasType('Test')); } - public function testQueryPlanOnInterfaceGroupingImplementorFields() : void + public function testQueryPlanGroupingImplementorFieldsForAbstractTypes() : void { $car = null; @@ -715,6 +715,30 @@ public function testQueryPlanOnInterfaceGroupingImplementorFields() : void }, ]); + $manualTransmission = new ObjectType([ + 'name' => 'ManualTransmission', + 'fields' => [ + 'speed' => Type::int(), + 'overdrive' => Type::boolean(), + ], + ]); + + $automaticTransmission = new ObjectType([ + 'name' => 'AutomaticTransmission', + 'fields' => [ + 'speed' => Type::int(), + 'sportMode' => Type::boolean(), + ], + ]); + + $transmission = new UnionType([ + 'name' => 'Transmission', + 'types' => [$manualTransmission, $automaticTransmission], + 'resolveType' => static function () use ($manualTransmission) { + return $manualTransmission; + }, + ]); + $car = new ObjectType([ 'name' => 'Car', 'fields' => [ @@ -722,6 +746,7 @@ public function testQueryPlanOnInterfaceGroupingImplementorFields() : void 'owner' => Type::string(), 'mark' => Type::string(), 'model' => Type::string(), + 'transmission' => $transmission, ], 'interfaces' => [$item], ]); @@ -744,6 +769,16 @@ public function testQueryPlanOnInterfaceGroupingImplementorFields() : void ... on Car { mark model + transmission { + ... on ManualTransmission { + speed + overdrive + } + ... on AutomaticTransmission { + speed + sportMode + } + } } ... on Building { city @@ -786,6 +821,43 @@ public function testQueryPlanOnInterfaceGroupingImplementorFields() : void 'fields' => [], 'args' => [], ], + 'transmission' => [ + 'type' => $transmission, + 'fields' => [], + 'args' => [], + 'implementors' => [ + 'ManualTransmission' => [ + 'type' => $manualTransmission, + 'fields' => [ + 'speed' => [ + 'type' => Type::int(), + 'fields' => [], + 'args' => [], + ], + 'overdrive' => [ + 'type' => Type::boolean(), + 'fields' => [], + 'args' => [], + ], + ], + ], + 'AutomaticTransmission' => [ + 'type' => $automaticTransmission, + 'fields' => [ + 'speed' => [ + 'type' => Type::int(), + 'fields' => [], + 'args' => [], + ], + 'sportMode' => [ + 'type' => Type::boolean(), + 'fields' => [], + 'args' => [], + ], + ], + ], + ], + ], ], ], 'Building' => [ @@ -806,9 +878,9 @@ public function testQueryPlanOnInterfaceGroupingImplementorFields() : void ], ]; - $expectedReferencedTypes = ['Car', 'Building', 'Item']; + $expectedReferencedTypes = ['ManualTransmission', 'AutomaticTransmission', 'Transmission', 'Car', 'Building', 'Item']; - $expectedReferencedFields = ['mark', 'model', 'city', 'address', 'id', 'owner']; + $expectedReferencedFields = ['speed', 'overdrive', 'sportMode', 'mark', 'model', 'transmission', 'city', 'address', 'id', 'owner']; $expectedItemSubFields = ['id', 'owner']; $expectedBuildingSubFields = ['city', 'address']; @@ -846,122 +918,4 @@ public function testQueryPlanOnInterfaceGroupingImplementorFields() : void self::assertEquals($expectedItemSubFields, $queryPlan->subFields('Item')); self::assertEquals($expectedBuildingSubFields, $queryPlan->subFields('Building')); } - - public function testQueryPlanOnUnionGroupingImplementorFields() : void - { - $car = new ObjectType([ - 'name' => 'Car', - 'fields' => [ - 'mark' => Type::string(), - 'model' => Type::string(), - ], - ]); - - $building = new ObjectType([ - 'name' => 'Building', - 'fields' => [ - 'city' => Type::string(), - 'address' => Type::string(), - ], - ]); - - $item = new UnionType([ - 'name' => 'Item', - 'types' => [$car, $building], - 'resolveType' => static function () use ($car) { - return $car; - }, - ]); - - $query = '{ - item { - ... on Car { - mark - model - } - ... on Building { - city - } - ...BuildingFragment - } - } - fragment BuildingFragment on Building { - address - }'; - - $expectedResult = [ - 'data' => ['item' => null], - ]; - - $expectedQueryPlan = [ - 'fields' => [], - 'implementors' => [ - 'Car' => [ - 'type' => $car, - 'fields' => [ - 'mark' => [ - 'type' => Type::string(), - 'fields' => [], - 'args' => [], - ], - 'model' => [ - 'type' => Type::string(), - 'fields' => [], - 'args' => [], - ], - ], - ], - 'Building' => [ - 'type' => $building, - 'fields' => [ - 'city' => [ - 'type' => Type::string(), - 'fields' => [], - 'args' => [], - ], - 'address' => [ - 'type' => Type::string(), - 'fields' => [], - 'args' => [], - ], - ], - ], - ], - ]; - - $expectedReferencedTypes = ['Car', 'Building', 'Item']; - - $expectedReferencedFields = ['mark', 'model', 'city', 'address']; - - $expectedBuildingSubFields = ['city', 'address']; - - $hasCalled = false; - /** @var QueryPlan $queryPlan */ - $queryPlan = null; - - $root = new ObjectType([ - 'name' => 'Query', - 'fields' => [ - 'item' => [ - 'type' => $item, - 'resolve' => static function ($value, $args, $context, ResolveInfo $info) use (&$hasCalled, &$queryPlan) { - $hasCalled = true; - $queryPlan = $info->lookAhead(['group-implementor-fields']); - - return null; - }, - ], - ], - ]); - - $schema = new Schema(['query' => $root]); - $result = GraphQL::executeQuery($schema, $query)->toArray(); - - self::assertTrue($hasCalled); - self::assertEquals($expectedResult, $result); - self::assertEquals($expectedQueryPlan, $queryPlan->queryPlan()); - self::assertEquals($expectedReferencedTypes, $queryPlan->getReferencedTypes()); - self::assertEquals($expectedReferencedFields, $queryPlan->getReferencedFields()); - self::assertEquals($expectedBuildingSubFields, $queryPlan->subFields('Building')); - } }