mirror of
https://github.com/retailcrm/graphql-php.git
synced 2025-02-06 15:59:24 +03:00
Added ability to decorate type configs in BuildSchema + made type creation lazy
This commit is contained in:
parent
3a8301f6c6
commit
09070485c1
@ -76,22 +76,27 @@ class BuildSchema
|
|||||||
* has no resolve methods, so execution will use default resolvers.
|
* has no resolve methods, so execution will use default resolvers.
|
||||||
*
|
*
|
||||||
* @param DocumentNode $ast
|
* @param DocumentNode $ast
|
||||||
|
* @param callable $typeConfigDecorator
|
||||||
* @return Schema
|
* @return Schema
|
||||||
* @throws Error
|
* @throws Error
|
||||||
*/
|
*/
|
||||||
public static function buildAST(DocumentNode $ast)
|
public static function buildAST(DocumentNode $ast, callable $typeConfigDecorator = null)
|
||||||
{
|
{
|
||||||
$builder = new self($ast);
|
$builder = new self($ast, $typeConfigDecorator);
|
||||||
return $builder->buildSchema();
|
return $builder->buildSchema();
|
||||||
}
|
}
|
||||||
|
|
||||||
private $ast;
|
private $ast;
|
||||||
private $innerTypeMap;
|
private $innerTypeMap;
|
||||||
private $nodeMap;
|
private $nodeMap;
|
||||||
|
private $typeConfigDecorator;
|
||||||
|
private $loadedTypeDefs;
|
||||||
|
|
||||||
public function __construct(DocumentNode $ast)
|
public function __construct(DocumentNode $ast, callable $typeConfigDecorator = null)
|
||||||
{
|
{
|
||||||
$this->ast = $ast;
|
$this->ast = $ast;
|
||||||
|
$this->typeConfigDecorator = $typeConfigDecorator;
|
||||||
|
$this->loadedTypeDefs = [];
|
||||||
}
|
}
|
||||||
|
|
||||||
public function buildSchema()
|
public function buildSchema()
|
||||||
@ -199,10 +204,6 @@ class BuildSchema
|
|||||||
'__TypeKind' => Introspection::_typeKind(),
|
'__TypeKind' => Introspection::_typeKind(),
|
||||||
];
|
];
|
||||||
|
|
||||||
$types = array_map(function($def) {
|
|
||||||
return $this->typeDefNamed($def->name->value);
|
|
||||||
}, $typeDefs);
|
|
||||||
|
|
||||||
$directives = array_map([$this, 'getDirective'], $directiveDefs);
|
$directives = array_map([$this, 'getDirective'], $directiveDefs);
|
||||||
|
|
||||||
// If specified directives were not explicitly declared, add them.
|
// If specified directives were not explicitly declared, add them.
|
||||||
@ -235,14 +236,21 @@ class BuildSchema
|
|||||||
'subscription' => $subscriptionTypeName ?
|
'subscription' => $subscriptionTypeName ?
|
||||||
$this->getObjectType($this->nodeMap[$subscriptionTypeName]) :
|
$this->getObjectType($this->nodeMap[$subscriptionTypeName]) :
|
||||||
null,
|
null,
|
||||||
'types' => $types,
|
'typeLoader' => function($name) {
|
||||||
|
return $this->typeDefNamed($name);
|
||||||
|
},
|
||||||
'directives' => $directives,
|
'directives' => $directives,
|
||||||
|
'types' => function() {
|
||||||
|
$types = [];
|
||||||
|
foreach ($this->nodeMap as $name => $def) {
|
||||||
|
if (!isset($this->loadedTypeDefs[$name])) {
|
||||||
|
$types[] = $this->typeDefNamed($def->name->value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return $types;
|
||||||
|
}
|
||||||
]);
|
]);
|
||||||
|
|
||||||
// Types in schema are loaded lazily, but we need full scan to ensure that schema is consistent
|
|
||||||
// Following statement will force Schema to scan all types and fields:
|
|
||||||
// TODO: replace this call with schema validator once it's ready
|
|
||||||
$schema->getTypeMap();
|
|
||||||
return $schema;
|
return $schema;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -251,9 +259,9 @@ class BuildSchema
|
|||||||
return new Directive([
|
return new Directive([
|
||||||
'name' => $directiveNode->name->value,
|
'name' => $directiveNode->name->value,
|
||||||
'description' => $this->getDescription($directiveNode),
|
'description' => $this->getDescription($directiveNode),
|
||||||
'locations' => array_map(function($node) {
|
'locations' => Utils::map($directiveNode->locations, function($node) {
|
||||||
return $node->value;
|
return $node->value;
|
||||||
}, $directiveNode->locations),
|
}),
|
||||||
'args' => $directiveNode->arguments ? FieldArgument::createMap($this->makeInputValues($directiveNode->arguments)) : null,
|
'args' => $directiveNode->arguments ? FieldArgument::createMap($this->makeInputValues($directiveNode->arguments)) : null,
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
@ -313,7 +321,45 @@ class BuildSchema
|
|||||||
throw new Error('Type "' . $typeName . '" not found in document.');
|
throw new Error('Type "' . $typeName . '" not found in document.');
|
||||||
}
|
}
|
||||||
|
|
||||||
$innerTypeDef = $this->makeSchemaDef($this->nodeMap[$typeName]);
|
$this->loadedTypeDefs[$typeName] = true;
|
||||||
|
|
||||||
|
$config = $this->makeSchemaDefConfig($this->nodeMap[$typeName]);
|
||||||
|
|
||||||
|
if ($this->typeConfigDecorator) {
|
||||||
|
$fn = $this->typeConfigDecorator;
|
||||||
|
try {
|
||||||
|
$config = $fn($this->nodeMap[$typeName], $config, $this->nodeMap);
|
||||||
|
} catch (\Exception $e) {
|
||||||
|
throw new Error(
|
||||||
|
"Type config decorator passed to " . (static::class) . " threw an error ".
|
||||||
|
"when building $typeName type: {$e->getMessage()}",
|
||||||
|
null,
|
||||||
|
null,
|
||||||
|
null,
|
||||||
|
null,
|
||||||
|
$e
|
||||||
|
);
|
||||||
|
} catch (\Throwable $e) {
|
||||||
|
throw new Error(
|
||||||
|
"Type config decorator passed to " . (static::class) . " threw an error ".
|
||||||
|
"when building $typeName type: {$e->getMessage()}",
|
||||||
|
null,
|
||||||
|
null,
|
||||||
|
null,
|
||||||
|
null,
|
||||||
|
$e
|
||||||
|
);
|
||||||
|
}
|
||||||
|
if (!is_array($config) || isset($config[0])) {
|
||||||
|
throw new Error(
|
||||||
|
"Type config decorator passed to " . (static::class) . " is expected to return an array, but got ".
|
||||||
|
Utils::getVariableType($config)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$innerTypeDef = $this->makeSchemaDef($this->nodeMap[$typeName], $config);
|
||||||
|
|
||||||
if (!$innerTypeDef) {
|
if (!$innerTypeDef) {
|
||||||
throw new Error("Nothing constructed for $typeName.");
|
throw new Error("Nothing constructed for $typeName.");
|
||||||
}
|
}
|
||||||
@ -321,38 +367,68 @@ class BuildSchema
|
|||||||
return $innerTypeDef;
|
return $innerTypeDef;
|
||||||
}
|
}
|
||||||
|
|
||||||
private function makeSchemaDef($def)
|
private function makeSchemaDefConfig($def)
|
||||||
{
|
{
|
||||||
if (!$def) {
|
if (!$def) {
|
||||||
throw new Error('def must be defined.');
|
throw new Error('def must be defined.');
|
||||||
}
|
}
|
||||||
switch ($def->kind) {
|
switch ($def->kind) {
|
||||||
case NodeKind::OBJECT_TYPE_DEFINITION:
|
case NodeKind::OBJECT_TYPE_DEFINITION:
|
||||||
return $this->makeTypeDef($def);
|
return $this->makeTypeDefConfig($def);
|
||||||
case NodeKind::INTERFACE_TYPE_DEFINITION:
|
case NodeKind::INTERFACE_TYPE_DEFINITION:
|
||||||
return $this->makeInterfaceDef($def);
|
return $this->makeInterfaceDefConfig($def);
|
||||||
case NodeKind::ENUM_TYPE_DEFINITION:
|
case NodeKind::ENUM_TYPE_DEFINITION:
|
||||||
return $this->makeEnumDef($def);
|
return $this->makeEnumDefConfig($def);
|
||||||
case NodeKind::UNION_TYPE_DEFINITION:
|
case NodeKind::UNION_TYPE_DEFINITION:
|
||||||
return $this->makeUnionDef($def);
|
return $this->makeUnionDefConfig($def);
|
||||||
case NodeKind::SCALAR_TYPE_DEFINITION:
|
case NodeKind::SCALAR_TYPE_DEFINITION:
|
||||||
return $this->makeScalarDef($def);
|
return $this->makeScalarDefConfig($def);
|
||||||
case NodeKind::INPUT_OBJECT_TYPE_DEFINITION:
|
case NodeKind::INPUT_OBJECT_TYPE_DEFINITION:
|
||||||
return $this->makeInputObjectDef($def);
|
return $this->makeInputObjectDefConfig($def);
|
||||||
default:
|
default:
|
||||||
throw new Error("Type kind of {$def->kind} not supported.");
|
throw new Error("Type kind of {$def->kind} not supported.");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private function makeTypeDef(ObjectTypeDefinitionNode $def)
|
private function makeSchemaDef($def, array $config = null)
|
||||||
|
{
|
||||||
|
if (!$def) {
|
||||||
|
throw new Error('def must be defined.');
|
||||||
|
}
|
||||||
|
|
||||||
|
$config = $config ?: $this->makeSchemaDefConfig($def);
|
||||||
|
|
||||||
|
switch ($def->kind) {
|
||||||
|
case NodeKind::OBJECT_TYPE_DEFINITION:
|
||||||
|
return new ObjectType($config);
|
||||||
|
case NodeKind::INTERFACE_TYPE_DEFINITION:
|
||||||
|
return new InterfaceType($config);
|
||||||
|
case NodeKind::ENUM_TYPE_DEFINITION:
|
||||||
|
return new EnumType($config);
|
||||||
|
case NodeKind::UNION_TYPE_DEFINITION:
|
||||||
|
return new UnionType($config);
|
||||||
|
case NodeKind::SCALAR_TYPE_DEFINITION:
|
||||||
|
return new CustomScalarType($config);
|
||||||
|
case NodeKind::INPUT_OBJECT_TYPE_DEFINITION:
|
||||||
|
return new InputObjectType($config);
|
||||||
|
default:
|
||||||
|
throw new Error("Type kind of {$def->kind} not supported.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private function makeTypeDefConfig(ObjectTypeDefinitionNode $def)
|
||||||
{
|
{
|
||||||
$typeName = $def->name->value;
|
$typeName = $def->name->value;
|
||||||
return new ObjectType([
|
return [
|
||||||
'name' => $typeName,
|
'name' => $typeName,
|
||||||
'description' => $this->getDescription($def),
|
'description' => $this->getDescription($def),
|
||||||
'fields' => function() use ($def) { return $this->makeFieldDefMap($def); },
|
'fields' => function() use ($def) {
|
||||||
'interfaces' => function() use ($def) { return $this->makeImplementedInterfaces($def); }
|
return $this->makeFieldDefMap($def);
|
||||||
]);
|
},
|
||||||
|
'interfaces' => function() use ($def) {
|
||||||
|
return $this->makeImplementedInterfaces($def);
|
||||||
|
}
|
||||||
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
private function makeFieldDefMap($def)
|
private function makeFieldDefMap($def)
|
||||||
@ -375,7 +451,12 @@ class BuildSchema
|
|||||||
|
|
||||||
private function makeImplementedInterfaces(ObjectTypeDefinitionNode $def)
|
private function makeImplementedInterfaces(ObjectTypeDefinitionNode $def)
|
||||||
{
|
{
|
||||||
return isset($def->interfaces) ? array_map([$this, 'produceInterfaceType'], $def->interfaces) : null;
|
if (isset($def->interfaces)) {
|
||||||
|
return Utils::map($def->interfaces, function ($iface) {
|
||||||
|
return $this->produceInterfaceType($iface);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
private function makeInputValues($values)
|
private function makeInputValues($values)
|
||||||
@ -400,20 +481,24 @@ class BuildSchema
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
private function makeInterfaceDef(InterfaceTypeDefinitionNode $def)
|
private function makeInterfaceDefConfig(InterfaceTypeDefinitionNode $def)
|
||||||
{
|
{
|
||||||
$typeName = $def->name->value;
|
$typeName = $def->name->value;
|
||||||
return new InterfaceType([
|
return [
|
||||||
'name' => $typeName,
|
'name' => $typeName,
|
||||||
'description' => $this->getDescription($def),
|
'description' => $this->getDescription($def),
|
||||||
'fields' => function() use ($def) { return $this->makeFieldDefMap($def); },
|
'fields' => function() use ($def) {
|
||||||
'resolveType' => [$this, 'cannotExecuteSchema']
|
return $this->makeFieldDefMap($def);
|
||||||
]);
|
},
|
||||||
|
'resolveType' => function() {
|
||||||
|
$this->cannotExecuteSchema();
|
||||||
|
}
|
||||||
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
private function makeEnumDef(EnumTypeDefinitionNode $def)
|
private function makeEnumDefConfig(EnumTypeDefinitionNode $def)
|
||||||
{
|
{
|
||||||
return new EnumType([
|
return [
|
||||||
'name' => $def->name->value,
|
'name' => $def->name->value,
|
||||||
'description' => $this->getDescription($def),
|
'description' => $this->getDescription($def),
|
||||||
'values' => Utils::keyValMap(
|
'values' => Utils::keyValMap(
|
||||||
@ -428,41 +513,49 @@ class BuildSchema
|
|||||||
];
|
];
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
]);
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
private function makeUnionDef(UnionTypeDefinitionNode $def)
|
private function makeUnionDefConfig(UnionTypeDefinitionNode $def)
|
||||||
{
|
{
|
||||||
return new UnionType([
|
return [
|
||||||
'name' => $def->name->value,
|
'name' => $def->name->value,
|
||||||
'description' => $this->getDescription($def),
|
'description' => $this->getDescription($def),
|
||||||
'types' => array_map([$this, 'produceObjectType'], $def->types),
|
'types' => Utils::map($def->types, function($typeNode) {
|
||||||
|
return $this->produceObjectType($typeNode);
|
||||||
|
}),
|
||||||
'resolveType' => [$this, 'cannotExecuteSchema']
|
'resolveType' => [$this, 'cannotExecuteSchema']
|
||||||
]);
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
private function makeScalarDef(ScalarTypeDefinitionNode $def)
|
private function makeScalarDefConfig(ScalarTypeDefinitionNode $def)
|
||||||
{
|
{
|
||||||
return new CustomScalarType([
|
return [
|
||||||
'name' => $def->name->value,
|
'name' => $def->name->value,
|
||||||
'description' => $this->getDescription($def),
|
'description' => $this->getDescription($def),
|
||||||
'serialize' => function() { return false; },
|
'serialize' => function() {
|
||||||
|
return false;
|
||||||
|
},
|
||||||
// Note: validation calls the parse functions to determine if a
|
// Note: validation calls the parse functions to determine if a
|
||||||
// literal value is correct. Returning null would cause use of custom
|
// literal value is correct. Returning null would cause use of custom
|
||||||
// scalars to always fail validation. Returning false causes them to
|
// scalars to always fail validation. Returning false causes them to
|
||||||
// always pass validation.
|
// always pass validation.
|
||||||
'parseValue' => function() { return false; },
|
'parseValue' => function() {
|
||||||
'parseLiteral' => function() { return false; }
|
return false;
|
||||||
]);
|
},
|
||||||
|
'parseLiteral' => function() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
private function makeInputObjectDef(InputObjectTypeDefinitionNode $def)
|
private function makeInputObjectDefConfig(InputObjectTypeDefinitionNode $def)
|
||||||
{
|
{
|
||||||
return new InputObjectType([
|
return [
|
||||||
'name' => $def->name->value,
|
'name' => $def->name->value,
|
||||||
'description' => $this->getDescription($def),
|
'description' => $this->getDescription($def),
|
||||||
'fields' => function() use ($def) { return $this->makeInputValues($def->fields); }
|
'fields' => function() use ($def) { return $this->makeInputValues($def->fields); }
|
||||||
]);
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -485,8 +578,8 @@ class BuildSchema
|
|||||||
public function getDescription($node)
|
public function getDescription($node)
|
||||||
{
|
{
|
||||||
$loc = $node->loc;
|
$loc = $node->loc;
|
||||||
if (!$loc) {
|
if (!$loc || !$loc->startToken) {
|
||||||
return;
|
return ;
|
||||||
}
|
}
|
||||||
$comments = [];
|
$comments = [];
|
||||||
$minSpaces = null;
|
$minSpaces = null;
|
||||||
@ -516,11 +609,12 @@ class BuildSchema
|
|||||||
* document.
|
* document.
|
||||||
*
|
*
|
||||||
* @param Source|string $source
|
* @param Source|string $source
|
||||||
|
* @param callable $typeConfigDecorator
|
||||||
* @return Schema
|
* @return Schema
|
||||||
*/
|
*/
|
||||||
public static function build($source)
|
public static function build($source, callable $typeConfigDecorator = null)
|
||||||
{
|
{
|
||||||
return self::buildAST(Parser::parse($source));
|
return self::buildAST(Parser::parse($source), $typeConfigDecorator);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Count the number of spaces on the starting side of a string.
|
// Count the number of spaces on the starting side of a string.
|
||||||
@ -529,7 +623,8 @@ class BuildSchema
|
|||||||
return strlen($str) - strlen(ltrim($str));
|
return strlen($str) - strlen(ltrim($str));
|
||||||
}
|
}
|
||||||
|
|
||||||
public function cannotExecuteSchema() {
|
public function cannotExecuteSchema()
|
||||||
|
{
|
||||||
throw new Error(
|
throw new Error(
|
||||||
'Generated Schema cannot use Interface or Union types for execution.'
|
'Generated Schema cannot use Interface or Union types for execution.'
|
||||||
);
|
);
|
||||||
|
@ -211,7 +211,7 @@ class TypeInfo
|
|||||||
}
|
}
|
||||||
if ($type instanceof ObjectType || $type instanceof InterfaceType || $type instanceof InputObjectType) {
|
if ($type instanceof ObjectType || $type instanceof InterfaceType || $type instanceof InputObjectType) {
|
||||||
foreach ((array) $type->getFields() as $fieldName => $field) {
|
foreach ((array) $type->getFields() as $fieldName => $field) {
|
||||||
if (isset($field->args)) {
|
if (!empty($field->args)) {
|
||||||
$fieldArgTypes = array_map(function(FieldArgument $arg) { return $arg->getType(); }, $field->args);
|
$fieldArgTypes = array_map(function(FieldArgument $arg) { return $arg->getType(); }, $field->args);
|
||||||
$nestedTypes = array_merge($nestedTypes, $fieldArgTypes);
|
$nestedTypes = array_merge($nestedTypes, $fieldArgTypes);
|
||||||
}
|
}
|
||||||
|
@ -2,6 +2,11 @@
|
|||||||
namespace GraphQL\Tests\Utils;
|
namespace GraphQL\Tests\Utils;
|
||||||
|
|
||||||
use GraphQL\GraphQL;
|
use GraphQL\GraphQL;
|
||||||
|
use GraphQL\Language\AST\EnumTypeDefinitionNode;
|
||||||
|
use GraphQL\Language\AST\InputObjectTypeDefinitionNode;
|
||||||
|
use GraphQL\Language\AST\InterfaceTypeDefinitionNode;
|
||||||
|
use GraphQL\Language\AST\ObjectTypeDefinitionNode;
|
||||||
|
use GraphQL\Language\AST\TypeNode;
|
||||||
use GraphQL\Language\Parser;
|
use GraphQL\Language\Parser;
|
||||||
use GraphQL\Utils\BuildSchema;
|
use GraphQL\Utils\BuildSchema;
|
||||||
use GraphQL\Utils\SchemaPrinter;
|
use GraphQL\Utils\SchemaPrinter;
|
||||||
@ -134,7 +139,7 @@ type Hello {
|
|||||||
}
|
}
|
||||||
';
|
';
|
||||||
$output = $this->cycleOutput($body);
|
$output = $this->cycleOutput($body);
|
||||||
$this->assertEquals($output, $body);
|
$this->assertEquals($body, $output);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -761,7 +766,8 @@ type Hello {
|
|||||||
}
|
}
|
||||||
';
|
';
|
||||||
$doc = Parser::parse($body);
|
$doc = Parser::parse($body);
|
||||||
BuildSchema::buildAST($doc);
|
$schema = BuildSchema::buildAST($doc);
|
||||||
|
$schema->getTypeMap();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -778,7 +784,8 @@ schema {
|
|||||||
type Hello implements Bar { }
|
type Hello implements Bar { }
|
||||||
';
|
';
|
||||||
$doc = Parser::parse($body);
|
$doc = Parser::parse($body);
|
||||||
BuildSchema::buildAST($doc);
|
$schema = BuildSchema::buildAST($doc);
|
||||||
|
$schema->getTypeMap();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -796,7 +803,8 @@ union TestUnion = Bar
|
|||||||
type Hello { testUnion: TestUnion }
|
type Hello { testUnion: TestUnion }
|
||||||
';
|
';
|
||||||
$doc = Parser::parse($body);
|
$doc = Parser::parse($body);
|
||||||
BuildSchema::buildAST($doc);
|
$schema = BuildSchema::buildAST($doc);
|
||||||
|
$schema->getTypeMap();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -920,4 +928,132 @@ type Repeated {
|
|||||||
$this->setExpectedException('GraphQL\Error\Error', 'Type "Repeated" was defined more than once.');
|
$this->setExpectedException('GraphQL\Error\Error', 'Type "Repeated" was defined more than once.');
|
||||||
BuildSchema::buildAST($doc);
|
BuildSchema::buildAST($doc);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function testSupportsTypeConfigDecorator()
|
||||||
|
{
|
||||||
|
$body = '
|
||||||
|
schema {
|
||||||
|
query: Query
|
||||||
|
}
|
||||||
|
|
||||||
|
type Query {
|
||||||
|
str: String
|
||||||
|
color: Color
|
||||||
|
hello: Hello
|
||||||
|
}
|
||||||
|
|
||||||
|
enum Color {
|
||||||
|
RED
|
||||||
|
GREEN
|
||||||
|
BLUE
|
||||||
|
}
|
||||||
|
|
||||||
|
interface Hello {
|
||||||
|
world: String
|
||||||
|
}
|
||||||
|
';
|
||||||
|
$doc = Parser::parse($body);
|
||||||
|
|
||||||
|
$decorated = [];
|
||||||
|
$calls = [];
|
||||||
|
|
||||||
|
$typeConfigDecorator = function($node, $defaultConfig, $allNodesMap) use (&$decorated, &$calls) {
|
||||||
|
$decorated[] = $node->name->value;
|
||||||
|
$calls[] = [$node, $defaultConfig, $allNodesMap];
|
||||||
|
return ['description' => 'My description of ' . $node->name->value] + $defaultConfig;
|
||||||
|
};
|
||||||
|
|
||||||
|
$schema = BuildSchema::buildAST($doc, $typeConfigDecorator);
|
||||||
|
$schema->getTypeMap();
|
||||||
|
$this->assertEquals(['Query', 'Color', 'Hello'], $decorated);
|
||||||
|
|
||||||
|
list($node, $defaultConfig, $allNodesMap) = $calls[0];
|
||||||
|
$this->assertInstanceOf(ObjectTypeDefinitionNode::class, $node);
|
||||||
|
$this->assertEquals('Query', $defaultConfig['name']);
|
||||||
|
$this->assertInstanceOf(\Closure::class, $defaultConfig['fields']);
|
||||||
|
$this->assertInstanceOf(\Closure::class, $defaultConfig['interfaces']);
|
||||||
|
$this->assertArrayHasKey('description', $defaultConfig);
|
||||||
|
$this->assertCount(4, $defaultConfig);
|
||||||
|
$this->assertEquals(array_keys($allNodesMap), ['Query', 'Color', 'Hello']);
|
||||||
|
$this->assertEquals('My description of Query', $schema->getType('Query')->description);
|
||||||
|
|
||||||
|
|
||||||
|
list($node, $defaultConfig, $allNodesMap) = $calls[1];
|
||||||
|
$this->assertInstanceOf(EnumTypeDefinitionNode::class, $node);
|
||||||
|
$this->assertEquals('Color', $defaultConfig['name']);
|
||||||
|
$enumValue = [
|
||||||
|
'description' => '',
|
||||||
|
'deprecationReason' => ''
|
||||||
|
];
|
||||||
|
$this->assertEquals([
|
||||||
|
'RED' => $enumValue,
|
||||||
|
'GREEN' => $enumValue,
|
||||||
|
'BLUE' => $enumValue,
|
||||||
|
], $defaultConfig['values']);
|
||||||
|
$this->assertCount(3, $defaultConfig);
|
||||||
|
$this->assertEquals(array_keys($allNodesMap), ['Query', 'Color', 'Hello']);
|
||||||
|
$this->assertEquals('My description of Color', $schema->getType('Color')->description);
|
||||||
|
|
||||||
|
list($node, $defaultConfig, $allNodesMap) = $calls[2];
|
||||||
|
$this->assertInstanceOf(InterfaceTypeDefinitionNode::class, $node);
|
||||||
|
$this->assertEquals('Hello', $defaultConfig['name']);
|
||||||
|
$this->assertInstanceOf(\Closure::class, $defaultConfig['fields']);
|
||||||
|
$this->assertInstanceOf(\Closure::class, $defaultConfig['resolveType']);
|
||||||
|
$this->assertArrayHasKey('description', $defaultConfig);
|
||||||
|
$this->assertCount(4, $defaultConfig);
|
||||||
|
$this->assertEquals(array_keys($allNodesMap), ['Query', 'Color', 'Hello']);
|
||||||
|
$this->assertEquals('My description of Hello', $schema->getType('Hello')->description);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testCreatesTypesLazily()
|
||||||
|
{
|
||||||
|
$body = '
|
||||||
|
schema {
|
||||||
|
query: Query
|
||||||
|
}
|
||||||
|
|
||||||
|
type Query {
|
||||||
|
str: String
|
||||||
|
color: Color
|
||||||
|
hello: Hello
|
||||||
|
}
|
||||||
|
|
||||||
|
enum Color {
|
||||||
|
RED
|
||||||
|
GREEN
|
||||||
|
BLUE
|
||||||
|
}
|
||||||
|
|
||||||
|
interface Hello {
|
||||||
|
world: String
|
||||||
|
}
|
||||||
|
|
||||||
|
type World implements Hello {
|
||||||
|
world: String
|
||||||
|
}
|
||||||
|
';
|
||||||
|
$doc = Parser::parse($body);
|
||||||
|
$created = [];
|
||||||
|
|
||||||
|
$typeConfigDecorator = function($node, $config) use (&$created) {
|
||||||
|
$created[] = $node->name->value;
|
||||||
|
return $config;
|
||||||
|
};
|
||||||
|
|
||||||
|
$schema = BuildSchema::buildAST($doc, $typeConfigDecorator);
|
||||||
|
$this->assertEquals(['Query'], $created);
|
||||||
|
|
||||||
|
$schema->getType('Color');
|
||||||
|
$this->assertEquals(['Query', 'Color'], $created);
|
||||||
|
|
||||||
|
$schema->getType('Hello');
|
||||||
|
$this->assertEquals(['Query', 'Color', 'Hello'], $created);
|
||||||
|
|
||||||
|
$types = $schema->getTypeMap();
|
||||||
|
$this->assertEquals(['Query', 'Color', 'Hello', 'World'], $created);
|
||||||
|
$this->assertArrayHasKey('Query', $types);
|
||||||
|
$this->assertArrayHasKey('Color', $types);
|
||||||
|
$this->assertArrayHasKey('Hello', $types);
|
||||||
|
$this->assertArrayHasKey('World', $types);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user