mirror of
https://github.com/retailcrm/graphql-php.git
synced 2024-11-25 14:26:08 +03:00
Separate utility for extractTypes
This commit is contained in:
parent
dd4acfd3b6
commit
416733b4db
@ -20,6 +20,8 @@ use GraphQL\Type\Definition\ListOfType;
|
||||
use GraphQL\Type\Definition\NonNull;
|
||||
use GraphQL\Type\Definition\ObjectType;
|
||||
use GraphQL\Type\Definition\Type;
|
||||
use GraphQL\Type\Definition\UnionType;
|
||||
use GraphQL\Type\Definition\WrappingType;
|
||||
use GraphQL\Type\Introspection;
|
||||
use GraphQL\Utils;
|
||||
|
||||
@ -162,6 +164,66 @@ class TypeInfo
|
||||
return $schema->getType($inputTypeNode->name->value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Given root type scans through all fields to find nested types. Returns array where keys are for type name
|
||||
* and value contains corresponding type instance.
|
||||
*
|
||||
* Example output:
|
||||
* [
|
||||
* 'String' => $instanceOfStringType,
|
||||
* 'MyType' => $instanceOfMyType,
|
||||
* ...
|
||||
* ]
|
||||
*
|
||||
* @param Type $type
|
||||
* @param array|null $typeMap
|
||||
* @return array
|
||||
*/
|
||||
public static function extractTypes($type, array $typeMap = null)
|
||||
{
|
||||
if (!$typeMap) {
|
||||
$typeMap = [];
|
||||
}
|
||||
if (!$type) {
|
||||
return $typeMap;
|
||||
}
|
||||
|
||||
if ($type instanceof WrappingType) {
|
||||
return self::extractTypes($type->getWrappedType(true), $typeMap);
|
||||
}
|
||||
|
||||
if (!empty($typeMap[$type->name])) {
|
||||
Utils::invariant(
|
||||
$typeMap[$type->name] === $type,
|
||||
"Schema must contain unique named types but contains multiple types named \"$type\"."
|
||||
);
|
||||
return $typeMap;
|
||||
}
|
||||
$typeMap[$type->name] = $type;
|
||||
|
||||
$nestedTypes = [];
|
||||
|
||||
if ($type instanceof UnionType) {
|
||||
$nestedTypes = $type->getTypes();
|
||||
}
|
||||
if ($type instanceof ObjectType) {
|
||||
$nestedTypes = array_merge($nestedTypes, $type->getInterfaces());
|
||||
}
|
||||
if ($type instanceof ObjectType || $type instanceof InterfaceType || $type instanceof InputObjectType) {
|
||||
foreach ((array) $type->getFields() as $fieldName => $field) {
|
||||
if (isset($field->args)) {
|
||||
$fieldArgTypes = array_map(function(FieldArgument $arg) { return $arg->getType(); }, $field->args);
|
||||
$nestedTypes = array_merge($nestedTypes, $fieldArgTypes);
|
||||
}
|
||||
$nestedTypes[] = $field->getType();
|
||||
}
|
||||
}
|
||||
foreach ($nestedTypes as $type) {
|
||||
$typeMap = self::extractTypes($type, $typeMap);
|
||||
}
|
||||
return $typeMap;
|
||||
}
|
||||
|
||||
/**
|
||||
* Not exactly the same as the executor's definition of getFieldDef, in this
|
||||
* statically evaluated environment we do not always have an Object type,
|
||||
|
338
tests/Utils/ExtractTypesTest.php
Normal file
338
tests/Utils/ExtractTypesTest.php
Normal file
@ -0,0 +1,338 @@
|
||||
<?php
|
||||
namespace Utils;
|
||||
|
||||
use GraphQL\Type\Definition\Config;
|
||||
use GraphQL\Type\Definition\InputObjectType;
|
||||
use GraphQL\Type\Definition\InterfaceType;
|
||||
use GraphQL\Type\Definition\ObjectType;
|
||||
use GraphQL\Type\Definition\Type;
|
||||
use GraphQL\Type\Definition\UnionType;
|
||||
use GraphQL\Utils\TypeInfo;
|
||||
|
||||
class ExtractTypesTest extends \PHPUnit_Framework_TestCase
|
||||
{
|
||||
/**
|
||||
* @var ObjectType
|
||||
*/
|
||||
private $query;
|
||||
|
||||
/**
|
||||
* @var ObjectType
|
||||
*/
|
||||
private $mutation;
|
||||
|
||||
/**
|
||||
* @var InterfaceType
|
||||
*/
|
||||
private $node;
|
||||
|
||||
/**
|
||||
* @var InterfaceType
|
||||
*/
|
||||
private $content;
|
||||
|
||||
/**
|
||||
* @var ObjectType
|
||||
*/
|
||||
private $blogStory;
|
||||
|
||||
/**
|
||||
* @var ObjectType
|
||||
*/
|
||||
private $link;
|
||||
|
||||
/**
|
||||
* @var ObjectType
|
||||
*/
|
||||
private $video;
|
||||
|
||||
/**
|
||||
* @var ObjectType
|
||||
*/
|
||||
private $videoMetadata;
|
||||
|
||||
/**
|
||||
* @var ObjectType
|
||||
*/
|
||||
private $comment;
|
||||
|
||||
/**
|
||||
* @var ObjectType
|
||||
*/
|
||||
private $user;
|
||||
|
||||
/**
|
||||
* @var ObjectType
|
||||
*/
|
||||
private $category;
|
||||
|
||||
/**
|
||||
* @var UnionType
|
||||
*/
|
||||
private $mention;
|
||||
|
||||
private $postStoryMutation;
|
||||
|
||||
private $postStoryMutationInput;
|
||||
|
||||
private $postCommentMutation;
|
||||
|
||||
private $postCommentMutationInput;
|
||||
|
||||
public function setUp()
|
||||
{
|
||||
Config::enableValidation(false);
|
||||
|
||||
$this->node = new InterfaceType([
|
||||
'name' => 'Node',
|
||||
'fields' => [
|
||||
'id' => Type::string()
|
||||
]
|
||||
]);
|
||||
|
||||
$this->content = new InterfaceType([
|
||||
'name' => 'Content',
|
||||
'fields' => function() {
|
||||
return [
|
||||
'title' => Type::string(),
|
||||
'body' => Type::string(),
|
||||
'author' => $this->user,
|
||||
'comments' => Type::listOf($this->comment),
|
||||
'categories' => Type::listOf($this->category)
|
||||
];
|
||||
}
|
||||
]);
|
||||
|
||||
$this->blogStory = new ObjectType([
|
||||
'name' => 'BlogStory',
|
||||
'interfaces' => [
|
||||
$this->node,
|
||||
$this->content
|
||||
],
|
||||
'fields' => function() {
|
||||
return [
|
||||
$this->node->getField('id'),
|
||||
$this->content->getField('title'),
|
||||
$this->content->getField('body'),
|
||||
$this->content->getField('author'),
|
||||
$this->content->getField('comments'),
|
||||
$this->content->getField('categories')
|
||||
];
|
||||
},
|
||||
]);
|
||||
|
||||
$this->link = new ObjectType([
|
||||
'name' => 'Link',
|
||||
'interfaces' => [
|
||||
$this->node,
|
||||
$this->content
|
||||
],
|
||||
'fields' => function() {
|
||||
return [
|
||||
$this->node->getField('id'),
|
||||
$this->content->getField('title'),
|
||||
$this->content->getField('body'),
|
||||
$this->content->getField('author'),
|
||||
$this->content->getField('comments'),
|
||||
$this->content->getField('categories'),
|
||||
'url' => Type::string()
|
||||
];
|
||||
},
|
||||
]);
|
||||
|
||||
$this->video = new ObjectType([
|
||||
'name' => 'Video',
|
||||
'interfaces' => [
|
||||
$this->node,
|
||||
$this->content
|
||||
],
|
||||
'fields' => function() {
|
||||
return [
|
||||
$this->node->getField('id'),
|
||||
$this->content->getField('title'),
|
||||
$this->content->getField('body'),
|
||||
$this->content->getField('author'),
|
||||
$this->content->getField('comments'),
|
||||
$this->content->getField('categories'),
|
||||
'streamUrl' => Type::string(),
|
||||
'downloadUrl' => Type::string(),
|
||||
'metadata' => $this->videoMetadata = new ObjectType([
|
||||
'name' => 'VideoMetadata',
|
||||
'fields' => [
|
||||
'lat' => Type::float(),
|
||||
'lng' => Type::float()
|
||||
]
|
||||
])
|
||||
];
|
||||
}
|
||||
]);
|
||||
|
||||
$this->comment = new ObjectType([
|
||||
'name' => 'Comment',
|
||||
'interfaces' => [
|
||||
$this->node
|
||||
],
|
||||
'fields' => function() {
|
||||
return [
|
||||
$this->node->getField('id'),
|
||||
'author' => $this->user,
|
||||
'text' => Type::string(),
|
||||
'replies' => Type::listOf($this->comment),
|
||||
'parent' => $this->comment,
|
||||
'content' => $this->content
|
||||
];
|
||||
}
|
||||
]);
|
||||
|
||||
$this->user = new ObjectType([
|
||||
'name' => 'User',
|
||||
'interfaces' => [
|
||||
$this->node
|
||||
],
|
||||
'fields' => function() {
|
||||
return [
|
||||
$this->node->getField('id'),
|
||||
'name' => Type::string(),
|
||||
];
|
||||
}
|
||||
]);
|
||||
|
||||
$this->category = new ObjectType([
|
||||
'name' => 'Category',
|
||||
'interfaces' => [
|
||||
$this->node
|
||||
],
|
||||
'fields' => function() {
|
||||
return [
|
||||
$this->node->getField('id'),
|
||||
'name' => Type::string()
|
||||
];
|
||||
}
|
||||
]);
|
||||
|
||||
$this->mention = new UnionType([
|
||||
'name' => 'Mention',
|
||||
'types' => [
|
||||
$this->user,
|
||||
$this->category
|
||||
]
|
||||
]);
|
||||
|
||||
$this->query = new ObjectType([
|
||||
'name' => 'Query',
|
||||
'fields' => [
|
||||
'viewer' => $this->user,
|
||||
'latestContent' => $this->content,
|
||||
'node' => $this->node,
|
||||
'mentions' => Type::listOf($this->mention)
|
||||
]
|
||||
]);
|
||||
|
||||
$this->mutation = new ObjectType([
|
||||
'name' => 'Mutation',
|
||||
'fields' => [
|
||||
'postStory' => [
|
||||
'type' => $this->postStoryMutation = new ObjectType([
|
||||
'name' => 'PostStoryMutation',
|
||||
'fields' => [
|
||||
'story' => $this->blogStory
|
||||
]
|
||||
]),
|
||||
'args' => [
|
||||
'input' => Type::nonNull($this->postStoryMutationInput = new InputObjectType([
|
||||
'name' => 'PostStoryMutationInput',
|
||||
'fields' => [
|
||||
'title' => Type::string(),
|
||||
'body' => Type::string(),
|
||||
'author' => Type::id(),
|
||||
'category' => Type::id()
|
||||
]
|
||||
])),
|
||||
'clientRequestId' => Type::string()
|
||||
]
|
||||
],
|
||||
'postComment' => [
|
||||
'type' => $this->postCommentMutation = new ObjectType([
|
||||
'name' => 'PostCommentMutation',
|
||||
'fields' => [
|
||||
'comment' => $this->comment
|
||||
]
|
||||
]),
|
||||
'args' => [
|
||||
'input' => Type::nonNull($this->postCommentMutationInput = new InputObjectType([
|
||||
'name' => 'PostCommentMutationInput',
|
||||
'fields' => [
|
||||
'text' => Type::nonNull(Type::string()),
|
||||
'author' => Type::nonNull(Type::id()),
|
||||
'content' => Type::id(),
|
||||
'parent' => Type::id()
|
||||
]
|
||||
])),
|
||||
'clientRequestId' => Type::string()
|
||||
]
|
||||
]
|
||||
]
|
||||
]);
|
||||
}
|
||||
|
||||
public function testExtractTypesFromQuery()
|
||||
{
|
||||
$expectedTypeMap = [
|
||||
'Query' => $this->query,
|
||||
'User' => $this->user,
|
||||
'Node' => $this->node,
|
||||
'String' => Type::string(),
|
||||
'Content' => $this->content,
|
||||
'Comment' => $this->comment,
|
||||
'Mention' => $this->mention,
|
||||
'Category' => $this->category,
|
||||
];
|
||||
|
||||
$actualTypeMap = TypeInfo::extractTypes($this->query);
|
||||
$this->assertEquals($expectedTypeMap, $actualTypeMap);
|
||||
}
|
||||
|
||||
public function testExtractTypesFromMutation()
|
||||
{
|
||||
$expectedTypeMap = [
|
||||
'Mutation' => $this->mutation,
|
||||
'User' => $this->user,
|
||||
'Node' => $this->node,
|
||||
'String' => Type::string(),
|
||||
'Content' => $this->content,
|
||||
'Comment' => $this->comment,
|
||||
'BlogStory' => $this->blogStory,
|
||||
'Category' => $this->category,
|
||||
'PostStoryMutationInput' => $this->postStoryMutationInput,
|
||||
'ID' => Type::id(),
|
||||
'PostStoryMutation' => $this->postStoryMutation,
|
||||
'PostCommentMutationInput' => $this->postCommentMutationInput,
|
||||
'PostCommentMutation' => $this->postCommentMutation,
|
||||
];
|
||||
|
||||
$actualTypeMap = TypeInfo::extractTypes($this->mutation);
|
||||
$this->assertEquals($expectedTypeMap, $actualTypeMap);
|
||||
}
|
||||
|
||||
public function testThrowsOnMultipleTypesWithSameName()
|
||||
{
|
||||
$otherUserType = new ObjectType([
|
||||
'name' => 'User',
|
||||
'fields' => []
|
||||
]);
|
||||
|
||||
$queryType = new ObjectType([
|
||||
'name' => 'Test',
|
||||
'fields' => [
|
||||
'otherUser' => $otherUserType,
|
||||
'user' => $this->user
|
||||
]
|
||||
]);
|
||||
|
||||
$this->setExpectedException(
|
||||
'\GraphQL\Error\InvariantViolation',
|
||||
"Schema must contain unique named types but contains multiple types named \"User\""
|
||||
);
|
||||
TypeInfo::extractTypes($queryType);
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user