From 84a52c6c760ca39780d02702d98b350105ada215 Mon Sep 17 00:00:00 2001 From: Vladimir Razuvaev Date: Wed, 19 Jun 2019 17:11:24 +0700 Subject: [PATCH] Provide a path with a correct list index to `resolveType` callback of the union and interface types (fixes #396) --- src/Executor/ReferenceExecutor.php | 1 + .../Executor/CoroutineExecutor.php | 5 +- tests/Regression/Issue396Test.php | 156 ++++++++++++++++++ 3 files changed, 160 insertions(+), 2 deletions(-) create mode 100644 tests/Regression/Issue396Test.php diff --git a/src/Executor/ReferenceExecutor.php b/src/Executor/ReferenceExecutor.php index 5c0353b..001dbd5 100644 --- a/src/Executor/ReferenceExecutor.php +++ b/src/Executor/ReferenceExecutor.php @@ -949,6 +949,7 @@ class ReferenceExecutor implements ExecutorImplementation foreach ($result as $item) { $fieldPath = $path; $fieldPath[] = $i++; + $info->path = $fieldPath; $completedItem = $this->completeValueCatchingError($itemType, $fieldNodes, $info, $fieldPath, $item); if (! $containsPromise && $this->getPromise($completedItem)) { $containsPromise = true; diff --git a/src/Experimental/Executor/CoroutineExecutor.php b/src/Experimental/Executor/CoroutineExecutor.php index d90035b..3aba3e2 100644 --- a/src/Experimental/Executor/CoroutineExecutor.php +++ b/src/Experimental/Executor/CoroutineExecutor.php @@ -620,8 +620,9 @@ class CoroutineExecutor implements Runtime, ExecutorImplementation foreach ($value as $itemValue) { ++$index; - $itemPath = $path; - $itemPath[] = $index; // !!! use arrays COW semantics + $itemPath = $path; + $itemPath[] = $index; // !!! use arrays COW semantics + $ctx->resolveInfo->path = $itemPath; try { if (! $this->completeValueFast($ctx, $itemType, $itemValue, $itemPath, $itemReturnValue)) { diff --git a/tests/Regression/Issue396Test.php b/tests/Regression/Issue396Test.php new file mode 100644 index 0000000..41d0b79 --- /dev/null +++ b/tests/Regression/Issue396Test.php @@ -0,0 +1,156 @@ + 'A', 'fields' => ['name' => Type::string()]]); + $b = new ObjectType(['name' => 'B', 'fields' => ['name' => Type::string()]]); + $c = new ObjectType(['name' => 'C', 'fields' => ['name' => Type::string()]]); + + $log = []; + + $unionResult = new UnionType([ + 'name' => 'UnionResult', + 'types' => [$a, $b, $c], + 'resolveType' => static function ($result, $root, ResolveInfo $info) use ($a, $b, $c, &$log) : Type { + $log[] = [$result, $info->path]; + if (stristr($result['name'], 'A')) { + return $a; + } + if (stristr($result['name'], 'B')) { + return $b; + } + if (stristr($result['name'], 'C')) { + return $c; + } + }, + ]); + + $exampleType = new ObjectType([ + 'name' => 'Example', + 'fields' => [ + 'field' => [ + 'type' => Type::nonNull(Type::listOf(Type::nonNull($unionResult))), + 'resolve' => static function () : array { + return [ + ['name' => 'A 1'], + ['name' => 'B 2'], + ['name' => 'C 3'], + ]; + }, + ], + ], + ]); + + $schema = new Schema(['query' => $exampleType]); + + $query = ' + query { + field { + ... on A { + name + } + ... on B { + name + } + ... on C { + name + } + } + } + '; + + GraphQL::executeQuery($schema, $query); + + $expected = [ + [['name' => 'A 1'], ['field', 0]], + [['name' => 'B 2'], ['field', 1]], + [['name' => 'C 3'], ['field', 2]], + ]; + self::assertEquals($expected, $log); + } + + public function testInterfaceResolveType() + { + $log = []; + + $interfaceResult = new InterfaceType([ + 'name' => 'InterfaceResult', + 'fields' => [ + 'name' => Type::string(), + ], + 'resolveType' => static function ($result, $root, ResolveInfo $info) use (&$a, &$b, &$c, &$log) : Type { + $log[] = [$result, $info->path]; + if (stristr($result['name'], 'A')) { + return $a; + } + if (stristr($result['name'], 'B')) { + return $b; + } + if (stristr($result['name'], 'C')) { + return $c; + } + }, + ]); + + $a = new ObjectType(['name' => 'A', 'fields' => ['name' => Type::string()], 'interfaces' => [$interfaceResult]]); + $b = new ObjectType(['name' => 'B', 'fields' => ['name' => Type::string()], 'interfaces' => [$interfaceResult]]); + $c = new ObjectType(['name' => 'C', 'fields' => ['name' => Type::string()], 'interfaces' => [$interfaceResult]]); + + $exampleType = new ObjectType([ + 'name' => 'Example', + 'fields' => [ + 'field' => [ + 'type' => Type::nonNull(Type::listOf(Type::nonNull($interfaceResult))), + 'resolve' => static function () : array { + return [ + ['name' => 'A 1'], + ['name' => 'B 2'], + ['name' => 'C 3'], + ]; + }, + ], + ], + ]); + + $schema = new Schema([ + 'query' => $exampleType, + 'types' => [$a, $b, $c], + ]); + + $query = ' + query { + field { + name + } + } + '; + + GraphQL::executeQuery($schema, $query); + + $expected = [ + [['name' => 'A 1'], ['field', 0]], + [['name' => 'B 2'], ['field', 1]], + [['name' => 'C 3'], ['field', 2]], + ]; + self::assertEquals($expected, $log); + } +}