Find this useful? Enter your email to receive occasional updates for securing PHP code.
Signing you up...
Thank you for signing up!
PHP Decode
<?php declare(strict_types=1); namespace GraphQL\Tests\Executor; use GraphQL\Deferred; u..
Decoded Output download
<?php declare(strict_types=1);
namespace GraphQL\Tests\Executor;
use GraphQL\Deferred;
use GraphQL\Executor\Executor;
use GraphQL\Language\Parser;
use GraphQL\Type\Definition\ObjectType;
use GraphQL\Type\Definition\ResolveInfo;
use GraphQL\Type\Definition\Type;
use GraphQL\Type\Schema;
use PHPUnit\Framework\TestCase;
final class DeferredFieldsTest extends TestCase
{
private ObjectType $userType;
private ObjectType $storyType;
private ObjectType $categoryType;
/** @var array<int, mixed> */
private array $paths = [];
/**
* @var array<
* int,
* array{
* id: int,
* authorId: int,
* title: string,
* categoryIds: array<int, int>
* }
* >
*/
private array $storyDataSource;
/**
* @var array<
* int,
* array{
* id: int,
* name: string,
* bestFriendId: int,
* }
* >
*/
private array $userDataSource;
/**
* @var array<
* int,
* array{
* id: int,
* name: string,
* topStoryId: int,
* }
* >
*/
private array $categoryDataSource;
private ObjectType $queryType;
public function setUp(): void
{
$this->storyDataSource = [
['id' => 1, 'authorId' => 1, 'title' => 'Story #1', 'categoryIds' => [2, 3]],
['id' => 2, 'authorId' => 2, 'title' => 'Story #2', 'categoryIds' => [1, 2]],
['id' => 3, 'authorId' => 3, 'title' => 'Story #3', 'categoryIds' => [2]],
['id' => 4, 'authorId' => 3, 'title' => 'Story #4', 'categoryIds' => [1]],
['id' => 5, 'authorId' => 1, 'title' => 'Story #5', 'categoryIds' => [3]],
['id' => 6, 'authorId' => 2, 'title' => 'Story #6', 'categoryIds' => [1]],
['id' => 7, 'authorId' => 3, 'title' => 'Story #7', 'categoryIds' => [2]],
['id' => 8, 'authorId' => 1, 'title' => 'Story #8', 'categoryIds' => [1, 2, 3]],
['id' => 9, 'authorId' => 2, 'title' => 'Story #9', 'categoryIds' => [2, 3]],
];
$this->userDataSource = [
['id' => 1, 'name' => 'John', 'bestFriendId' => 4],
['id' => 2, 'name' => 'Jane', 'bestFriendId' => 3],
['id' => 3, 'name' => 'Joe', 'bestFriendId' => 2],
['id' => 4, 'name' => 'Dirk', 'bestFriendId' => 1],
];
$this->categoryDataSource = [
['id' => 1, 'name' => 'Category #1', 'topStoryId' => 8],
['id' => 2, 'name' => 'Category #2', 'topStoryId' => 3],
['id' => 3, 'name' => 'Category #3', 'topStoryId' => 9],
];
$this->userType = new ObjectType([
'name' => 'User',
'fields' => fn (): array => [
'name' => [
'type' => Type::string(),
'resolve' => function ($user, $args, $context, ResolveInfo $info) {
$this->paths[] = $info->path;
return $user['name'];
},
],
'bestFriend' => [
'type' => $this->userType,
'resolve' => function ($user, $args, $context, ResolveInfo $info): Deferred {
$this->paths[] = $info->path;
return new Deferred(function () use ($user) {
$this->paths[] = 'deferred-for-best-friend-of-' . $user['id'];
return $this->findUserById($user['bestFriendId']);
});
},
],
],
]);
$this->storyType = new ObjectType([
'name' => 'Story',
'fields' => [
'title' => [
'type' => Type::string(),
'resolve' => function ($story, $args, $context, ResolveInfo $info) {
$this->paths[] = $info->path;
return $story['title'];
},
],
'author' => [
'type' => $this->userType,
'resolve' => function ($story, $args, $context, ResolveInfo $info): Deferred {
$this->paths[] = $info->path;
return new Deferred(function () use ($story) {
$this->paths[] = 'deferred-for-story-' . $story['id'] . '-author';
return $this->findUserById($story['authorId']);
});
},
],
],
]);
$this->categoryType = new ObjectType([
'name' => 'Category',
'fields' => [
'name' => [
'type' => Type::string(),
'resolve' => function ($category, array $args, $context, ResolveInfo $info) {
$this->paths[] = $info->path;
return $category['name'];
},
],
'stories' => [
'type' => Type::listOf($this->storyType),
'resolve' => function ($category, array $args, $context, ResolveInfo $info): array {
$this->paths[] = $info->path;
return rray_filter(
$this->storyDataSource,
static fn ($story): bool => \in_array($category['id'], $story['categoryIds'], true)
);
},
],
'topStory' => [
'type' => $this->storyType,
'resolve' => function ($category, array $args, $context, ResolveInfo $info): Deferred {
$this->paths[] = $info->path;
return new Deferred(function () use ($category): ?array {
$this->paths[] = 'deferred-for-category-' . $category['id'] . '-topStory';
return $this->findStoryById($category['topStoryId']);
});
},
],
'topStoryAuthor' => [
'type' => $this->userType,
'resolve' => function ($category, array $args, $context, ResolveInfo $info): Deferred {
$this->paths[] = $info->path;
return new Deferred(function () use ($category): Deferred {
$this->paths[] = 'deferred-for-category-' . $category['id'] . '-topStoryAuthor1';
$story = $this->findStoryById($category['topStoryId']);
return new Deferred(function () use ($category, $story) {
$this->paths[] = 'deferred-for-category-' . $category['id'] . '-topStoryAuthor2';
if ($story === null) {
return null;
}
return $this->findUserById($story['authorId']);
});
});
},
],
],
]);
$this->queryType = new ObjectType([
'name' => 'Query',
'fields' => [
'topStories' => [
'type' => Type::listOf($this->storyType),
'resolve' => function ($rootValue, array $args, $context, ResolveInfo $info): array {
$this->paths[] = $info->path;
return rray_filter(
$this->storyDataSource,
static fn ($story): bool => $story['id'] % 2 === 1
);
},
],
'featuredCategory' => [
'type' => $this->categoryType,
'resolve' => function ($rootValue, array $args, $context, ResolveInfo $info): array {
$this->paths[] = $info->path;
return $this->categoryDataSource[0];
},
],
'categories' => [
'type' => Type::listOf($this->categoryType),
'resolve' => function ($rootValue, array $args, $context, ResolveInfo $info): array {
$this->paths[] = $info->path;
return $this->categoryDataSource;
},
],
],
]);
parent::setUp();
}
public function testDeferredFields(): void
{
$query = Parser::parse('
{
topStories {
title
author {
name
}
}
featuredCategory {
stories {
title
author {
name
}
}
}
}
');
$schema = new Schema([
'query' => $this->queryType,
]);
$expected = [
'data' => [
'topStories' => [
['title' => 'Story #1', 'author' => ['name' => 'John']],
['title' => 'Story #3', 'author' => ['name' => 'Joe']],
['title' => 'Story #5', 'author' => ['name' => 'John']],
['title' => 'Story #7', 'author' => ['name' => 'Joe']],
['title' => 'Story #9', 'author' => ['name' => 'Jane']],
],
'featuredCategory' => [
'stories' => [
['title' => 'Story #2', 'author' => ['name' => 'Jane']],
['title' => 'Story #4', 'author' => ['name' => 'Joe']],
['title' => 'Story #6', 'author' => ['name' => 'Jane']],
['title' => 'Story #8', 'author' => ['name' => 'John']],
],
],
],
];
$result = Executor::execute($schema, $query);
self::assertSame($expected, $result->toArray());
$this->assertPathsMatch([
['topStories'],
['topStories', 0, 'title'],
['topStories', 0, 'author'],
['topStories', 1, 'title'],
['topStories', 1, 'author'],
['topStories', 2, 'title'],
['topStories', 2, 'author'],
['topStories', 3, 'title'],
['topStories', 3, 'author'],
['topStories', 4, 'title'],
['topStories', 4, 'author'],
['featuredCategory'],
['featuredCategory', 'stories'],
['featuredCategory', 'stories', 0, 'title'],
['featuredCategory', 'stories', 0, 'author'],
['featuredCategory', 'stories', 1, 'title'],
['featuredCategory', 'stories', 1, 'author'],
['featuredCategory', 'stories', 2, 'title'],
['featuredCategory', 'stories', 2, 'author'],
['featuredCategory', 'stories', 3, 'title'],
['featuredCategory', 'stories', 3, 'author'],
'deferred-for-story-1-author',
'deferred-for-story-3-author',
'deferred-for-story-5-author',
'deferred-for-story-7-author',
'deferred-for-story-9-author',
'deferred-for-story-2-author',
'deferred-for-story-4-author',
'deferred-for-story-6-author',
'deferred-for-story-8-author',
['topStories', 0, 'author', 'name'],
['topStories', 1, 'author', 'name'],
['topStories', 2, 'author', 'name'],
['topStories', 3, 'author', 'name'],
['topStories', 4, 'author', 'name'],
['featuredCategory', 'stories', 0, 'author', 'name'],
['featuredCategory', 'stories', 1, 'author', 'name'],
['featuredCategory', 'stories', 2, 'author', 'name'],
['featuredCategory', 'stories', 3, 'author', 'name'],
]);
}
public function testNestedDeferredFields(): void
{
$query = Parser::parse('
{
categories {
name
topStory {
title
author {
name
bestFriend {
name
}
}
}
}
}
');
$schema = new Schema([
'query' => $this->queryType,
]);
$author1 = ['name' => 'John', 'bestFriend' => ['name' => 'Dirk']];
$author2 = ['name' => 'Jane', 'bestFriend' => ['name' => 'Joe']];
$author3 = ['name' => 'Joe', 'bestFriend' => ['name' => 'Jane']];
$expected = [
'data' => [
'categories' => [
['name' => 'Category #1', 'topStory' => ['title' => 'Story #8', 'author' => $author1]],
['name' => 'Category #2', 'topStory' => ['title' => 'Story #3', 'author' => $author3]],
['name' => 'Category #3', 'topStory' => ['title' => 'Story #9', 'author' => $author2]],
],
],
];
$result = Executor::execute($schema, $query);
self::assertSame($expected, $result->toArray());
$this->assertPathsMatch([
['categories'],
['categories', 0, 'name'],
['categories', 0, 'topStory'],
['categories', 1, 'name'],
['categories', 1, 'topStory'],
['categories', 2, 'name'],
['categories', 2, 'topStory'],
'deferred-for-category-1-topStory',
'deferred-for-category-2-topStory',
'deferred-for-category-3-topStory',
['categories', 0, 'topStory', 'title'],
['categories', 0, 'topStory', 'author'],
['categories', 1, 'topStory', 'title'],
['categories', 1, 'topStory', 'author'],
['categories', 2, 'topStory', 'title'],
['categories', 2, 'topStory', 'author'],
'deferred-for-story-8-author',
'deferred-for-story-3-author',
'deferred-for-story-9-author',
['categories', 0, 'topStory', 'author', 'name'],
['categories', 0, 'topStory', 'author', 'bestFriend'],
['categories', 1, 'topStory', 'author', 'name'],
['categories', 1, 'topStory', 'author', 'bestFriend'],
['categories', 2, 'topStory', 'author', 'name'],
['categories', 2, 'topStory', 'author', 'bestFriend'],
'deferred-for-best-friend-of-1',
'deferred-for-best-friend-of-3',
'deferred-for-best-friend-of-2',
['categories', 0, 'topStory', 'author', 'bestFriend', 'name'],
['categories', 1, 'topStory', 'author', 'bestFriend', 'name'],
['categories', 2, 'topStory', 'author', 'bestFriend', 'name'],
]);
}
public function testComplexRecursiveDeferredFields(): void
{
$complexType = new ObjectType([
'name' => 'ComplexType',
'fields' => function () use (&$complexType): array {
return [
'sync' => [
'type' => Type::string(),
'resolve' => function ($complexType, $args, $context, ResolveInfo $info): string {
$this->paths[] = $info->path;
return 'sync';
},
],
'deferred' => [
'type' => Type::string(),
'resolve' => function ($complexType, $args, $context, ResolveInfo $info): Deferred {
$this->paths[] = $info->path;
return new Deferred(function () use ($info): string {
$this->paths[] = ['!dfd for: ', $info->path];
return 'deferred';
});
},
],
'nest' => [
'type' => $complexType,
'resolve' => function ($complexType, $args, $context, ResolveInfo $info): array {
$this->paths[] = $info->path;
return [];
},
],
'deferredNest' => [
'type' => $complexType,
'resolve' => function ($complexType, $args, $context, ResolveInfo $info): Deferred {
$this->paths[] = $info->path;
return new Deferred(function () use ($info): array {
$this->paths[] = ['!dfd nest for: ', $info->path];
return [];
});
},
],
];
},
]);
$schema = new Schema(['query' => $complexType]);
$query = Parser::parse('
{
nest {
sync
deferred
nest {
sync
deferred
}
deferredNest {
sync
deferred
}
}
deferredNest {
sync
deferred
nest {
sync
deferred
}
deferredNest {
sync
deferred
}
}
}
');
$result = Executor::execute($schema, $query);
$expected = [
'data' => [
'nest' => [
'sync' => 'sync',
'deferred' => 'deferred',
'nest' => [
'sync' => 'sync',
'deferred' => 'deferred',
],
'deferredNest' => [
'sync' => 'sync',
'deferred' => 'deferred',
],
],
'deferredNest' => [
'sync' => 'sync',
'deferred' => 'deferred',
'nest' => [
'sync' => 'sync',
'deferred' => 'deferred',
],
'deferredNest' => [
'sync' => 'sync',
'deferred' => 'deferred',
],
],
],
];
self::assertSame($expected, $result->toArray());
$this->assertPathsMatch([
['nest'],
['nest', 'sync'],
['nest', 'deferred'],
['nest', 'nest'],
['nest', 'nest', 'sync'],
['nest', 'nest', 'deferred'],
['nest', 'deferredNest'],
['deferredNest'],
['!dfd for: ', ['nest', 'deferred']],
['!dfd for: ', ['nest', 'nest', 'deferred']],
['!dfd nest for: ', ['nest', 'deferredNest']],
['!dfd nest for: ', ['deferredNest']],
['nest', 'deferredNest', 'sync'],
['nest', 'deferredNest', 'deferred'],
['deferredNest', 'sync'],
['deferredNest', 'deferred'],
['deferredNest', 'nest'],
['deferredNest', 'nest', 'sync'],
['deferredNest', 'nest', 'deferred'],
['deferredNest', 'deferredNest'],
['!dfd for: ', ['nest', 'deferredNest', 'deferred']],
['!dfd for: ', ['deferredNest', 'deferred']],
['!dfd for: ', ['deferredNest', 'nest', 'deferred']],
['!dfd nest for: ', ['deferredNest', 'deferredNest']],
['deferredNest', 'deferredNest', 'sync'],
['deferredNest', 'deferredNest', 'deferred'],
['!dfd for: ', ['deferredNest', 'deferredNest', 'deferred']],
]);
}
public function testDeferredChaining(): void
{
$schema = new Schema([
'query' => $this->queryType,
]);
$query = Parser::parse('
{
categories {
name
topStory {
title
author {
name
}
}
topStoryAuthor {
name
}
}
}
');
$author1 = ['name' => 'John'/* , 'bestFriend' => ['name' => 'Dirk'] */];
$author2 = ['name' => 'Jane'/* , 'bestFriend' => ['name' => 'Joe'] */];
$author3 = ['name' => 'Joe'/* , 'bestFriend' => ['name' => 'Jane'] */];
$story1 = ['title' => 'Story #8', 'author' => $author1];
$story2 = ['title' => 'Story #3', 'author' => $author3];
$story3 = ['title' => 'Story #9', 'author' => $author2];
$result = Executor::execute($schema, $query);
$expected = [
'data' => [
'categories' => [
['name' => 'Category #1', 'topStory' => $story1, 'topStoryAuthor' => $author1],
['name' => 'Category #2', 'topStory' => $story2, 'topStoryAuthor' => $author3],
['name' => 'Category #3', 'topStory' => $story3, 'topStoryAuthor' => $author2],
],
],
];
self::assertSame($expected, $result->toArray());
$this->assertPathsMatch([
['categories'],
['categories', 0, 'name'],
['categories', 0, 'topStory'],
['categories', 0, 'topStoryAuthor'],
['categories', 1, 'name'],
['categories', 1, 'topStory'],
['categories', 1, 'topStoryAuthor'],
['categories', 2, 'name'],
['categories', 2, 'topStory'],
['categories', 2, 'topStoryAuthor'],
'deferred-for-category-1-topStory',
'deferred-for-category-1-topStoryAuthor1',
'deferred-for-category-2-topStory',
'deferred-for-category-2-topStoryAuthor1',
'deferred-for-category-3-topStory',
'deferred-for-category-3-topStoryAuthor1',
['categories', 0, 'topStory', 'title'],
['categories', 0, 'topStory', 'author'],
'deferred-for-category-1-topStoryAuthor2',
['categories', 1, 'topStory', 'title'],
['categories', 1, 'topStory', 'author'],
'deferred-for-category-2-topStoryAuthor2',
['categories', 2, 'topStory', 'title'],
['categories', 2, 'topStory', 'author'],
'deferred-for-category-3-topStoryAuthor2',
'deferred-for-story-8-author',
'deferred-for-story-3-author',
'deferred-for-story-9-author',
['categories', 0, 'topStory', 'author', 'name'],
['categories', 0, 'topStoryAuthor', 'name'],
['categories', 1, 'topStory', 'author', 'name'],
['categories', 1, 'topStoryAuthor', 'name'],
['categories', 2, 'topStory', 'author', 'name'],
['categories', 2, 'topStoryAuthor', 'name'],
]);
}
/** @return array<string, mixed>|null */
private function findStoryById(int $id): ?array
{
foreach ($this->storyDataSource as $story) {
if ($story['id'] === $id) {
return $story;
}
}
return null;
}
/** @return array<string, mixed>|null */
private function findUserById(int $id): ?array
{
foreach ($this->userDataSource as $user) {
if ($user['id'] === $id) {
return $user;
}
}
return null;
}
/**
* @param array<mixed> $expectedPaths
*
* @throws \JsonException
*/
private function assertPathsMatch(array $expectedPaths): void
{
self::assertCount(\count($expectedPaths), $this->paths);
foreach ($expectedPaths as $expectedPath) {
self::assertContains($expectedPath, $this->paths, 'Missing path: ' . json_encode($expectedPath, JSON_THROW_ON_ERROR));
}
}
}
?>
Did this file decode correctly?
Original Code
<?php declare(strict_types=1);
namespace GraphQL\Tests\Executor;
use GraphQL\Deferred;
use GraphQL\Executor\Executor;
use GraphQL\Language\Parser;
use GraphQL\Type\Definition\ObjectType;
use GraphQL\Type\Definition\ResolveInfo;
use GraphQL\Type\Definition\Type;
use GraphQL\Type\Schema;
use PHPUnit\Framework\TestCase;
final class DeferredFieldsTest extends TestCase
{
private ObjectType $userType;
private ObjectType $storyType;
private ObjectType $categoryType;
/** @var array<int, mixed> */
private array $paths = [];
/**
* @var array<
* int,
* array{
* id: int,
* authorId: int,
* title: string,
* categoryIds: array<int, int>
* }
* >
*/
private array $storyDataSource;
/**
* @var array<
* int,
* array{
* id: int,
* name: string,
* bestFriendId: int,
* }
* >
*/
private array $userDataSource;
/**
* @var array<
* int,
* array{
* id: int,
* name: string,
* topStoryId: int,
* }
* >
*/
private array $categoryDataSource;
private ObjectType $queryType;
public function setUp(): void
{
$this->storyDataSource = [
['id' => 1, 'authorId' => 1, 'title' => 'Story #1', 'categoryIds' => [2, 3]],
['id' => 2, 'authorId' => 2, 'title' => 'Story #2', 'categoryIds' => [1, 2]],
['id' => 3, 'authorId' => 3, 'title' => 'Story #3', 'categoryIds' => [2]],
['id' => 4, 'authorId' => 3, 'title' => 'Story #4', 'categoryIds' => [1]],
['id' => 5, 'authorId' => 1, 'title' => 'Story #5', 'categoryIds' => [3]],
['id' => 6, 'authorId' => 2, 'title' => 'Story #6', 'categoryIds' => [1]],
['id' => 7, 'authorId' => 3, 'title' => 'Story #7', 'categoryIds' => [2]],
['id' => 8, 'authorId' => 1, 'title' => 'Story #8', 'categoryIds' => [1, 2, 3]],
['id' => 9, 'authorId' => 2, 'title' => 'Story #9', 'categoryIds' => [2, 3]],
];
$this->userDataSource = [
['id' => 1, 'name' => 'John', 'bestFriendId' => 4],
['id' => 2, 'name' => 'Jane', 'bestFriendId' => 3],
['id' => 3, 'name' => 'Joe', 'bestFriendId' => 2],
['id' => 4, 'name' => 'Dirk', 'bestFriendId' => 1],
];
$this->categoryDataSource = [
['id' => 1, 'name' => 'Category #1', 'topStoryId' => 8],
['id' => 2, 'name' => 'Category #2', 'topStoryId' => 3],
['id' => 3, 'name' => 'Category #3', 'topStoryId' => 9],
];
$this->userType = new ObjectType([
'name' => 'User',
'fields' => fn (): array => [
'name' => [
'type' => Type::string(),
'resolve' => function ($user, $args, $context, ResolveInfo $info) {
$this->paths[] = $info->path;
return $user['name'];
},
],
'bestFriend' => [
'type' => $this->userType,
'resolve' => function ($user, $args, $context, ResolveInfo $info): Deferred {
$this->paths[] = $info->path;
return new Deferred(function () use ($user) {
$this->paths[] = 'deferred-for-best-friend-of-' . $user['id'];
return $this->findUserById($user['bestFriendId']);
});
},
],
],
]);
$this->storyType = new ObjectType([
'name' => 'Story',
'fields' => [
'title' => [
'type' => Type::string(),
'resolve' => function ($story, $args, $context, ResolveInfo $info) {
$this->paths[] = $info->path;
return $story['title'];
},
],
'author' => [
'type' => $this->userType,
'resolve' => function ($story, $args, $context, ResolveInfo $info): Deferred {
$this->paths[] = $info->path;
return new Deferred(function () use ($story) {
$this->paths[] = 'deferred-for-story-' . $story['id'] . '-author';
return $this->findUserById($story['authorId']);
});
},
],
],
]);
$this->categoryType = new ObjectType([
'name' => 'Category',
'fields' => [
'name' => [
'type' => Type::string(),
'resolve' => function ($category, array $args, $context, ResolveInfo $info) {
$this->paths[] = $info->path;
return $category['name'];
},
],
'stories' => [
'type' => Type::listOf($this->storyType),
'resolve' => function ($category, array $args, $context, ResolveInfo $info): array {
$this->paths[] = $info->path;
return \array_filter(
$this->storyDataSource,
static fn ($story): bool => \in_array($category['id'], $story['categoryIds'], true)
);
},
],
'topStory' => [
'type' => $this->storyType,
'resolve' => function ($category, array $args, $context, ResolveInfo $info): Deferred {
$this->paths[] = $info->path;
return new Deferred(function () use ($category): ?array {
$this->paths[] = 'deferred-for-category-' . $category['id'] . '-topStory';
return $this->findStoryById($category['topStoryId']);
});
},
],
'topStoryAuthor' => [
'type' => $this->userType,
'resolve' => function ($category, array $args, $context, ResolveInfo $info): Deferred {
$this->paths[] = $info->path;
return new Deferred(function () use ($category): Deferred {
$this->paths[] = 'deferred-for-category-' . $category['id'] . '-topStoryAuthor1';
$story = $this->findStoryById($category['topStoryId']);
return new Deferred(function () use ($category, $story) {
$this->paths[] = 'deferred-for-category-' . $category['id'] . '-topStoryAuthor2';
if ($story === null) {
return null;
}
return $this->findUserById($story['authorId']);
});
});
},
],
],
]);
$this->queryType = new ObjectType([
'name' => 'Query',
'fields' => [
'topStories' => [
'type' => Type::listOf($this->storyType),
'resolve' => function ($rootValue, array $args, $context, ResolveInfo $info): array {
$this->paths[] = $info->path;
return \array_filter(
$this->storyDataSource,
static fn ($story): bool => $story['id'] % 2 === 1
);
},
],
'featuredCategory' => [
'type' => $this->categoryType,
'resolve' => function ($rootValue, array $args, $context, ResolveInfo $info): array {
$this->paths[] = $info->path;
return $this->categoryDataSource[0];
},
],
'categories' => [
'type' => Type::listOf($this->categoryType),
'resolve' => function ($rootValue, array $args, $context, ResolveInfo $info): array {
$this->paths[] = $info->path;
return $this->categoryDataSource;
},
],
],
]);
parent::setUp();
}
public function testDeferredFields(): void
{
$query = Parser::parse('
{
topStories {
title
author {
name
}
}
featuredCategory {
stories {
title
author {
name
}
}
}
}
');
$schema = new Schema([
'query' => $this->queryType,
]);
$expected = [
'data' => [
'topStories' => [
['title' => 'Story #1', 'author' => ['name' => 'John']],
['title' => 'Story #3', 'author' => ['name' => 'Joe']],
['title' => 'Story #5', 'author' => ['name' => 'John']],
['title' => 'Story #7', 'author' => ['name' => 'Joe']],
['title' => 'Story #9', 'author' => ['name' => 'Jane']],
],
'featuredCategory' => [
'stories' => [
['title' => 'Story #2', 'author' => ['name' => 'Jane']],
['title' => 'Story #4', 'author' => ['name' => 'Joe']],
['title' => 'Story #6', 'author' => ['name' => 'Jane']],
['title' => 'Story #8', 'author' => ['name' => 'John']],
],
],
],
];
$result = Executor::execute($schema, $query);
self::assertSame($expected, $result->toArray());
$this->assertPathsMatch([
['topStories'],
['topStories', 0, 'title'],
['topStories', 0, 'author'],
['topStories', 1, 'title'],
['topStories', 1, 'author'],
['topStories', 2, 'title'],
['topStories', 2, 'author'],
['topStories', 3, 'title'],
['topStories', 3, 'author'],
['topStories', 4, 'title'],
['topStories', 4, 'author'],
['featuredCategory'],
['featuredCategory', 'stories'],
['featuredCategory', 'stories', 0, 'title'],
['featuredCategory', 'stories', 0, 'author'],
['featuredCategory', 'stories', 1, 'title'],
['featuredCategory', 'stories', 1, 'author'],
['featuredCategory', 'stories', 2, 'title'],
['featuredCategory', 'stories', 2, 'author'],
['featuredCategory', 'stories', 3, 'title'],
['featuredCategory', 'stories', 3, 'author'],
'deferred-for-story-1-author',
'deferred-for-story-3-author',
'deferred-for-story-5-author',
'deferred-for-story-7-author',
'deferred-for-story-9-author',
'deferred-for-story-2-author',
'deferred-for-story-4-author',
'deferred-for-story-6-author',
'deferred-for-story-8-author',
['topStories', 0, 'author', 'name'],
['topStories', 1, 'author', 'name'],
['topStories', 2, 'author', 'name'],
['topStories', 3, 'author', 'name'],
['topStories', 4, 'author', 'name'],
['featuredCategory', 'stories', 0, 'author', 'name'],
['featuredCategory', 'stories', 1, 'author', 'name'],
['featuredCategory', 'stories', 2, 'author', 'name'],
['featuredCategory', 'stories', 3, 'author', 'name'],
]);
}
public function testNestedDeferredFields(): void
{
$query = Parser::parse('
{
categories {
name
topStory {
title
author {
name
bestFriend {
name
}
}
}
}
}
');
$schema = new Schema([
'query' => $this->queryType,
]);
$author1 = ['name' => 'John', 'bestFriend' => ['name' => 'Dirk']];
$author2 = ['name' => 'Jane', 'bestFriend' => ['name' => 'Joe']];
$author3 = ['name' => 'Joe', 'bestFriend' => ['name' => 'Jane']];
$expected = [
'data' => [
'categories' => [
['name' => 'Category #1', 'topStory' => ['title' => 'Story #8', 'author' => $author1]],
['name' => 'Category #2', 'topStory' => ['title' => 'Story #3', 'author' => $author3]],
['name' => 'Category #3', 'topStory' => ['title' => 'Story #9', 'author' => $author2]],
],
],
];
$result = Executor::execute($schema, $query);
self::assertSame($expected, $result->toArray());
$this->assertPathsMatch([
['categories'],
['categories', 0, 'name'],
['categories', 0, 'topStory'],
['categories', 1, 'name'],
['categories', 1, 'topStory'],
['categories', 2, 'name'],
['categories', 2, 'topStory'],
'deferred-for-category-1-topStory',
'deferred-for-category-2-topStory',
'deferred-for-category-3-topStory',
['categories', 0, 'topStory', 'title'],
['categories', 0, 'topStory', 'author'],
['categories', 1, 'topStory', 'title'],
['categories', 1, 'topStory', 'author'],
['categories', 2, 'topStory', 'title'],
['categories', 2, 'topStory', 'author'],
'deferred-for-story-8-author',
'deferred-for-story-3-author',
'deferred-for-story-9-author',
['categories', 0, 'topStory', 'author', 'name'],
['categories', 0, 'topStory', 'author', 'bestFriend'],
['categories', 1, 'topStory', 'author', 'name'],
['categories', 1, 'topStory', 'author', 'bestFriend'],
['categories', 2, 'topStory', 'author', 'name'],
['categories', 2, 'topStory', 'author', 'bestFriend'],
'deferred-for-best-friend-of-1',
'deferred-for-best-friend-of-3',
'deferred-for-best-friend-of-2',
['categories', 0, 'topStory', 'author', 'bestFriend', 'name'],
['categories', 1, 'topStory', 'author', 'bestFriend', 'name'],
['categories', 2, 'topStory', 'author', 'bestFriend', 'name'],
]);
}
public function testComplexRecursiveDeferredFields(): void
{
$complexType = new ObjectType([
'name' => 'ComplexType',
'fields' => function () use (&$complexType): array {
return [
'sync' => [
'type' => Type::string(),
'resolve' => function ($complexType, $args, $context, ResolveInfo $info): string {
$this->paths[] = $info->path;
return 'sync';
},
],
'deferred' => [
'type' => Type::string(),
'resolve' => function ($complexType, $args, $context, ResolveInfo $info): Deferred {
$this->paths[] = $info->path;
return new Deferred(function () use ($info): string {
$this->paths[] = ['!dfd for: ', $info->path];
return 'deferred';
});
},
],
'nest' => [
'type' => $complexType,
'resolve' => function ($complexType, $args, $context, ResolveInfo $info): array {
$this->paths[] = $info->path;
return [];
},
],
'deferredNest' => [
'type' => $complexType,
'resolve' => function ($complexType, $args, $context, ResolveInfo $info): Deferred {
$this->paths[] = $info->path;
return new Deferred(function () use ($info): array {
$this->paths[] = ['!dfd nest for: ', $info->path];
return [];
});
},
],
];
},
]);
$schema = new Schema(['query' => $complexType]);
$query = Parser::parse('
{
nest {
sync
deferred
nest {
sync
deferred
}
deferredNest {
sync
deferred
}
}
deferredNest {
sync
deferred
nest {
sync
deferred
}
deferredNest {
sync
deferred
}
}
}
');
$result = Executor::execute($schema, $query);
$expected = [
'data' => [
'nest' => [
'sync' => 'sync',
'deferred' => 'deferred',
'nest' => [
'sync' => 'sync',
'deferred' => 'deferred',
],
'deferredNest' => [
'sync' => 'sync',
'deferred' => 'deferred',
],
],
'deferredNest' => [
'sync' => 'sync',
'deferred' => 'deferred',
'nest' => [
'sync' => 'sync',
'deferred' => 'deferred',
],
'deferredNest' => [
'sync' => 'sync',
'deferred' => 'deferred',
],
],
],
];
self::assertSame($expected, $result->toArray());
$this->assertPathsMatch([
['nest'],
['nest', 'sync'],
['nest', 'deferred'],
['nest', 'nest'],
['nest', 'nest', 'sync'],
['nest', 'nest', 'deferred'],
['nest', 'deferredNest'],
['deferredNest'],
['!dfd for: ', ['nest', 'deferred']],
['!dfd for: ', ['nest', 'nest', 'deferred']],
['!dfd nest for: ', ['nest', 'deferredNest']],
['!dfd nest for: ', ['deferredNest']],
['nest', 'deferredNest', 'sync'],
['nest', 'deferredNest', 'deferred'],
['deferredNest', 'sync'],
['deferredNest', 'deferred'],
['deferredNest', 'nest'],
['deferredNest', 'nest', 'sync'],
['deferredNest', 'nest', 'deferred'],
['deferredNest', 'deferredNest'],
['!dfd for: ', ['nest', 'deferredNest', 'deferred']],
['!dfd for: ', ['deferredNest', 'deferred']],
['!dfd for: ', ['deferredNest', 'nest', 'deferred']],
['!dfd nest for: ', ['deferredNest', 'deferredNest']],
['deferredNest', 'deferredNest', 'sync'],
['deferredNest', 'deferredNest', 'deferred'],
['!dfd for: ', ['deferredNest', 'deferredNest', 'deferred']],
]);
}
public function testDeferredChaining(): void
{
$schema = new Schema([
'query' => $this->queryType,
]);
$query = Parser::parse('
{
categories {
name
topStory {
title
author {
name
}
}
topStoryAuthor {
name
}
}
}
');
$author1 = ['name' => 'John'/* , 'bestFriend' => ['name' => 'Dirk'] */];
$author2 = ['name' => 'Jane'/* , 'bestFriend' => ['name' => 'Joe'] */];
$author3 = ['name' => 'Joe'/* , 'bestFriend' => ['name' => 'Jane'] */];
$story1 = ['title' => 'Story #8', 'author' => $author1];
$story2 = ['title' => 'Story #3', 'author' => $author3];
$story3 = ['title' => 'Story #9', 'author' => $author2];
$result = Executor::execute($schema, $query);
$expected = [
'data' => [
'categories' => [
['name' => 'Category #1', 'topStory' => $story1, 'topStoryAuthor' => $author1],
['name' => 'Category #2', 'topStory' => $story2, 'topStoryAuthor' => $author3],
['name' => 'Category #3', 'topStory' => $story3, 'topStoryAuthor' => $author2],
],
],
];
self::assertSame($expected, $result->toArray());
$this->assertPathsMatch([
['categories'],
['categories', 0, 'name'],
['categories', 0, 'topStory'],
['categories', 0, 'topStoryAuthor'],
['categories', 1, 'name'],
['categories', 1, 'topStory'],
['categories', 1, 'topStoryAuthor'],
['categories', 2, 'name'],
['categories', 2, 'topStory'],
['categories', 2, 'topStoryAuthor'],
'deferred-for-category-1-topStory',
'deferred-for-category-1-topStoryAuthor1',
'deferred-for-category-2-topStory',
'deferred-for-category-2-topStoryAuthor1',
'deferred-for-category-3-topStory',
'deferred-for-category-3-topStoryAuthor1',
['categories', 0, 'topStory', 'title'],
['categories', 0, 'topStory', 'author'],
'deferred-for-category-1-topStoryAuthor2',
['categories', 1, 'topStory', 'title'],
['categories', 1, 'topStory', 'author'],
'deferred-for-category-2-topStoryAuthor2',
['categories', 2, 'topStory', 'title'],
['categories', 2, 'topStory', 'author'],
'deferred-for-category-3-topStoryAuthor2',
'deferred-for-story-8-author',
'deferred-for-story-3-author',
'deferred-for-story-9-author',
['categories', 0, 'topStory', 'author', 'name'],
['categories', 0, 'topStoryAuthor', 'name'],
['categories', 1, 'topStory', 'author', 'name'],
['categories', 1, 'topStoryAuthor', 'name'],
['categories', 2, 'topStory', 'author', 'name'],
['categories', 2, 'topStoryAuthor', 'name'],
]);
}
/** @return array<string, mixed>|null */
private function findStoryById(int $id): ?array
{
foreach ($this->storyDataSource as $story) {
if ($story['id'] === $id) {
return $story;
}
}
return null;
}
/** @return array<string, mixed>|null */
private function findUserById(int $id): ?array
{
foreach ($this->userDataSource as $user) {
if ($user['id'] === $id) {
return $user;
}
}
return null;
}
/**
* @param array<mixed> $expectedPaths
*
* @throws \JsonException
*/
private function assertPathsMatch(array $expectedPaths): void
{
self::assertCount(\count($expectedPaths), $this->paths);
foreach ($expectedPaths as $expectedPath) {
self::assertContains($expectedPath, $this->paths, 'Missing path: ' . json_encode($expectedPath, JSON_THROW_ON_ERROR));
}
}
}
Function Calls
None |
Stats
MD5 | 1304e11a449f8abb453b4969755335b0 |
Eval Count | 0 |
Decode Time | 134 ms |