mirror of
https://github.com/retailcrm/graphql-php.git
synced 2025-02-06 07:49:24 +03:00
Extracted lazy schema test; minor related refactoring
This commit is contained in:
parent
f47db61907
commit
b9d3a11785
@ -6,7 +6,7 @@ final class Warning
|
|||||||
const NAME_WARNING = 1;
|
const NAME_WARNING = 1;
|
||||||
const ASSIGN_WARNING = 2;
|
const ASSIGN_WARNING = 2;
|
||||||
const CONFIG_WARNING = 4;
|
const CONFIG_WARNING = 4;
|
||||||
const RESOLVE_TYPE_WARNING = 8;
|
const FULL_SCHEMA_SCAN_WARNING = 8;
|
||||||
const CONFIG_DEPRECATION_WARNING = 16;
|
const CONFIG_DEPRECATION_WARNING = 16;
|
||||||
const NOT_A_TYPE = 32;
|
const NOT_A_TYPE = 32;
|
||||||
|
|
||||||
|
@ -1062,13 +1062,13 @@ class Executor
|
|||||||
$runtimeType = $returnType->resolveType($result, $exeContext->contextValue, $info);
|
$runtimeType = $returnType->resolveType($result, $exeContext->contextValue, $info);
|
||||||
|
|
||||||
if (null === $runtimeType) {
|
if (null === $runtimeType) {
|
||||||
if ($returnType instanceof InterfaceType) {
|
if ($returnType instanceof InterfaceType && $info->schema->getConfig()->typeLoader) {
|
||||||
Warning::warnOnce(
|
Warning::warnOnce(
|
||||||
"GraphQL Interface Type `{$returnType->name}` returned `null` from it`s `resolveType` function ".
|
"GraphQL Interface Type `{$returnType->name}` returned `null` from it`s `resolveType` function ".
|
||||||
'for value: ' . Utils::printSafe($result) . '. Switching to slow resolution method using `isTypeOf` ' .
|
'for value: ' . Utils::printSafe($result) . '. Switching to slow resolution method using `isTypeOf` ' .
|
||||||
'of all possible implementations. It degrades query performance significantly. '.
|
'of all possible implementations. It requires full schema scan and degrades query performance significantly. '.
|
||||||
' Make sure your `resolveType` always returns valid implementation or throws.',
|
' Make sure your `resolveType` always returns valid implementation or throws.',
|
||||||
Warning::RESOLVE_TYPE_WARNING
|
Warning::FULL_SCHEMA_SCAN_WARNING
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
$runtimeType = self::defaultTypeResolver($result, $exeContext->contextValue, $info, $returnType);
|
$runtimeType = self::defaultTypeResolver($result, $exeContext->contextValue, $info, $returnType);
|
||||||
|
@ -123,6 +123,7 @@ class Schema
|
|||||||
$this->resolvedTypes[$config->subscription->name] = $config->subscription;
|
$this->resolvedTypes[$config->subscription->name] = $config->subscription;
|
||||||
}
|
}
|
||||||
if (!$this->config->typeLoader) {
|
if (!$this->config->typeLoader) {
|
||||||
|
// Perform full scan of the schema
|
||||||
$this->getTypeMap();
|
$this->getTypeMap();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -199,40 +200,44 @@ class Schema
|
|||||||
*/
|
*/
|
||||||
private function collectAllTypes()
|
private function collectAllTypes()
|
||||||
{
|
{
|
||||||
$initialTypes = $this->resolvedTypes;
|
|
||||||
|
|
||||||
$typeMap = [];
|
$typeMap = [];
|
||||||
foreach ($initialTypes as $type) {
|
foreach ($this->resolvedTypes as $type) {
|
||||||
$typeMap = TypeInfo::extractTypes($type, $typeMap);
|
$typeMap = TypeInfo::extractTypes($type, $typeMap);
|
||||||
}
|
}
|
||||||
|
foreach ($this->resolveAdditionalTypes() as $type) {
|
||||||
|
$typeMap = TypeInfo::extractTypes($type, $typeMap);
|
||||||
|
}
|
||||||
|
return $typeMap + Type::getInternalTypes() + Introspection::getTypes();
|
||||||
|
}
|
||||||
|
|
||||||
if ($this->config->types) {
|
/**
|
||||||
$types = $this->config->types;
|
* @return \Generator
|
||||||
|
*/
|
||||||
|
private function resolveAdditionalTypes()
|
||||||
|
{
|
||||||
|
$types = $this->config->types ?: [];
|
||||||
|
|
||||||
if (is_callable($types)) {
|
if (is_callable($types)) {
|
||||||
$types = $types();
|
$types = $types();
|
||||||
}
|
|
||||||
|
|
||||||
if (!is_array($types) && !$types instanceof \Traversable) {
|
|
||||||
throw new InvariantViolation(sprintf(
|
|
||||||
'Schema types callable must return array or instance of Traversable but got: %s',
|
|
||||||
Utils::getVariableType($types)
|
|
||||||
));
|
|
||||||
}
|
|
||||||
|
|
||||||
foreach ($types as $index => $type) {
|
|
||||||
if (!$type instanceof Type) {
|
|
||||||
throw new InvariantViolation(
|
|
||||||
'Each entry of schema types must be instance of GraphQL\Type\Definition\Type but entry at %s is %s',
|
|
||||||
$index,
|
|
||||||
Utils::printSafe($type)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
$typeMap = TypeInfo::extractTypes($type, $typeMap);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return $typeMap + Type::getInternalTypes() + Introspection::getTypes();
|
if (!is_array($types) && !$types instanceof \Traversable) {
|
||||||
|
throw new InvariantViolation(sprintf(
|
||||||
|
'Schema types callable must return array or instance of Traversable but got: %s',
|
||||||
|
Utils::getVariableType($types)
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach ($types as $index => $type) {
|
||||||
|
if (!$type instanceof Type) {
|
||||||
|
throw new InvariantViolation(
|
||||||
|
'Each entry of schema types must be instance of GraphQL\Type\Definition\Type but entry at %s is %s',
|
||||||
|
$index,
|
||||||
|
Utils::printSafe($type)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
yield $type;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -87,9 +87,9 @@ class AbstractPromiseTest extends \PHPUnit_Framework_TestCase
|
|||||||
}
|
}
|
||||||
}';
|
}';
|
||||||
|
|
||||||
Warning::suppress(Warning::RESOLVE_TYPE_WARNING);
|
Warning::suppress(Warning::FULL_SCHEMA_SCAN_WARNING);
|
||||||
$result = GraphQL::execute($schema, $query);
|
$result = GraphQL::execute($schema, $query);
|
||||||
Warning::enable(Warning::RESOLVE_TYPE_WARNING);
|
Warning::enable(Warning::FULL_SCHEMA_SCAN_WARNING);
|
||||||
|
|
||||||
$expected = [
|
$expected = [
|
||||||
'data' => [
|
'data' => [
|
||||||
@ -174,9 +174,9 @@ class AbstractPromiseTest extends \PHPUnit_Framework_TestCase
|
|||||||
}
|
}
|
||||||
}';
|
}';
|
||||||
|
|
||||||
Warning::suppress(Warning::RESOLVE_TYPE_WARNING);
|
Warning::suppress(Warning::FULL_SCHEMA_SCAN_WARNING);
|
||||||
$result = GraphQL::execute($schema, $query);
|
$result = GraphQL::execute($schema, $query);
|
||||||
Warning::enable(Warning::RESOLVE_TYPE_WARNING);
|
Warning::enable(Warning::FULL_SCHEMA_SCAN_WARNING);
|
||||||
|
|
||||||
$expected = [
|
$expected = [
|
||||||
'data' => [
|
'data' => [
|
||||||
|
@ -3,14 +3,11 @@ namespace GraphQL\Tests\Executor;
|
|||||||
|
|
||||||
require_once __DIR__ . '/TestClasses.php';
|
require_once __DIR__ . '/TestClasses.php';
|
||||||
|
|
||||||
use GraphQL\Error\Warning;
|
|
||||||
use GraphQL\Executor\ExecutionResult;
|
use GraphQL\Executor\ExecutionResult;
|
||||||
use GraphQL\Executor\Executor;
|
use GraphQL\Executor\Executor;
|
||||||
use GraphQL\Error\FormattedError;
|
|
||||||
use GraphQL\GraphQL;
|
use GraphQL\GraphQL;
|
||||||
use GraphQL\Language\Parser;
|
use GraphQL\Language\Parser;
|
||||||
use GraphQL\Language\SourceLocation;
|
use GraphQL\Type\Schema;
|
||||||
use GraphQL\Schema;
|
|
||||||
use GraphQL\Type\Definition\InterfaceType;
|
use GraphQL\Type\Definition\InterfaceType;
|
||||||
use GraphQL\Type\Definition\ObjectType;
|
use GraphQL\Type\Definition\ObjectType;
|
||||||
use GraphQL\Type\Definition\Type;
|
use GraphQL\Type\Definition\Type;
|
||||||
@ -90,21 +87,8 @@ class AbstractTest extends \PHPUnit_Framework_TestCase
|
|||||||
]
|
]
|
||||||
]);
|
]);
|
||||||
|
|
||||||
Warning::suppress(Warning::RESOLVE_TYPE_WARNING);
|
|
||||||
$result = Executor::execute($schema, Parser::parse($query));
|
$result = Executor::execute($schema, Parser::parse($query));
|
||||||
$this->assertEquals($expected, $result);
|
$this->assertEquals($expected, $result);
|
||||||
|
|
||||||
Warning::enable(Warning::RESOLVE_TYPE_WARNING);
|
|
||||||
$result = Executor::execute($schema, Parser::parse($query));
|
|
||||||
$this->assertEquals(1, count($result->errors));
|
|
||||||
$this->assertInstanceOf('PHPUnit_Framework_Error_Warning', $result->errors[0]->getPrevious());
|
|
||||||
|
|
||||||
$this->assertEquals(
|
|
||||||
'GraphQL Interface Type `Pet` returned `null` from it`s `resolveType` function for value: '.
|
|
||||||
'instance of GraphQL\Tests\Executor\Dog. Switching to slow resolution method using `isTypeOf` of '.
|
|
||||||
'all possible implementations. It degrades query performance significantly. '.
|
|
||||||
'Make sure your `resolveType` always returns valid implementation or throws.',
|
|
||||||
$result->errors[0]->getMessage());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
116
tests/Executor/ExecutorLazySchemaTest.php
Normal file
116
tests/Executor/ExecutorLazySchemaTest.php
Normal file
@ -0,0 +1,116 @@
|
|||||||
|
<?php
|
||||||
|
namespace GraphQL\Tests\Executor;
|
||||||
|
|
||||||
|
require_once __DIR__ . '/TestClasses.php';
|
||||||
|
|
||||||
|
use GraphQL\Error\Warning;
|
||||||
|
use GraphQL\Executor\ExecutionResult;
|
||||||
|
use GraphQL\Executor\Executor;
|
||||||
|
use GraphQL\Language\Parser;
|
||||||
|
use GraphQL\Type\Definition\InterfaceType;
|
||||||
|
use GraphQL\Type\Definition\ObjectType;
|
||||||
|
use GraphQL\Type\Definition\Type;
|
||||||
|
use GraphQL\Type\Schema;
|
||||||
|
|
||||||
|
class ExecutorLazySchemaTest extends \PHPUnit_Framework_TestCase
|
||||||
|
{
|
||||||
|
public function testWarnsAboutSlowIsTypeOfForLazySchema()
|
||||||
|
{
|
||||||
|
// isTypeOf used to resolve runtime type for Interface
|
||||||
|
$petType = new InterfaceType([
|
||||||
|
'name' => 'Pet',
|
||||||
|
'fields' => function() {
|
||||||
|
return [
|
||||||
|
'name' => ['type' => Type::string()]
|
||||||
|
];
|
||||||
|
}
|
||||||
|
]);
|
||||||
|
|
||||||
|
// Added to interface type when defined
|
||||||
|
$dogType = new ObjectType([
|
||||||
|
'name' => 'Dog',
|
||||||
|
'interfaces' => [$petType],
|
||||||
|
'isTypeOf' => function($obj) { return $obj instanceof Dog; },
|
||||||
|
'fields' => function() {
|
||||||
|
return [
|
||||||
|
'name' => ['type' => Type::string()],
|
||||||
|
'woofs' => ['type' => Type::boolean()]
|
||||||
|
];
|
||||||
|
}
|
||||||
|
]);
|
||||||
|
|
||||||
|
$catType = new ObjectType([
|
||||||
|
'name' => 'Cat',
|
||||||
|
'interfaces' => [$petType],
|
||||||
|
'isTypeOf' => function ($obj) {
|
||||||
|
return $obj instanceof Cat;
|
||||||
|
},
|
||||||
|
'fields' => function() {
|
||||||
|
return [
|
||||||
|
'name' => ['type' => Type::string()],
|
||||||
|
'meows' => ['type' => Type::boolean()],
|
||||||
|
];
|
||||||
|
}
|
||||||
|
]);
|
||||||
|
|
||||||
|
$schema = new Schema([
|
||||||
|
'query' => new ObjectType([
|
||||||
|
'name' => 'Query',
|
||||||
|
'fields' => [
|
||||||
|
'pets' => [
|
||||||
|
'type' => Type::listOf($petType),
|
||||||
|
'resolve' => function () {
|
||||||
|
return [new Dog('Odie', true), new Cat('Garfield', false)];
|
||||||
|
}
|
||||||
|
]
|
||||||
|
]
|
||||||
|
]),
|
||||||
|
'types' => [$catType, $dogType],
|
||||||
|
'typeLoader' => function($name) use ($dogType, $petType, $catType) {
|
||||||
|
switch ($name) {
|
||||||
|
case 'Dog':
|
||||||
|
return $dogType;
|
||||||
|
case 'Pet':
|
||||||
|
return $petType;
|
||||||
|
case 'Cat':
|
||||||
|
return $catType;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]);
|
||||||
|
|
||||||
|
$query = '{
|
||||||
|
pets {
|
||||||
|
name
|
||||||
|
... on Dog {
|
||||||
|
woofs
|
||||||
|
}
|
||||||
|
... on Cat {
|
||||||
|
meows
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}';
|
||||||
|
|
||||||
|
$expected = new ExecutionResult([
|
||||||
|
'pets' => [
|
||||||
|
['name' => 'Odie', 'woofs' => true],
|
||||||
|
['name' => 'Garfield', 'meows' => false]
|
||||||
|
],
|
||||||
|
]);
|
||||||
|
|
||||||
|
Warning::suppress(Warning::FULL_SCHEMA_SCAN_WARNING);
|
||||||
|
$result = Executor::execute($schema, Parser::parse($query));
|
||||||
|
$this->assertEquals($expected, $result);
|
||||||
|
|
||||||
|
Warning::enable(Warning::FULL_SCHEMA_SCAN_WARNING);
|
||||||
|
$result = Executor::execute($schema, Parser::parse($query));
|
||||||
|
$this->assertEquals(1, count($result->errors));
|
||||||
|
$this->assertInstanceOf('PHPUnit_Framework_Error_Warning', $result->errors[0]->getPrevious());
|
||||||
|
|
||||||
|
$this->assertEquals(
|
||||||
|
'GraphQL Interface Type `Pet` returned `null` from it`s `resolveType` function for value: instance of '.
|
||||||
|
'GraphQL\Tests\Executor\Dog. Switching to slow resolution method using `isTypeOf` of all possible '.
|
||||||
|
'implementations. It requires full schema scan and degrades query performance significantly. '.
|
||||||
|
'Make sure your `resolveType` always returns valid implementation or throws.',
|
||||||
|
$result->errors[0]->getMessage());
|
||||||
|
}
|
||||||
|
}
|
@ -256,9 +256,9 @@ class UnionInterfaceTest extends \PHPUnit_Framework_TestCase
|
|||||||
]
|
]
|
||||||
];
|
];
|
||||||
|
|
||||||
Warning::suppress(Warning::RESOLVE_TYPE_WARNING);
|
Warning::suppress(Warning::FULL_SCHEMA_SCAN_WARNING);
|
||||||
$this->assertEquals($expected, Executor::execute($this->schema, $ast, $this->john)->toArray());
|
$this->assertEquals($expected, Executor::execute($this->schema, $ast, $this->john)->toArray());
|
||||||
Warning::enable(Warning::RESOLVE_TYPE_WARNING);
|
Warning::enable(Warning::FULL_SCHEMA_SCAN_WARNING);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -294,9 +294,9 @@ class UnionInterfaceTest extends \PHPUnit_Framework_TestCase
|
|||||||
]
|
]
|
||||||
];
|
];
|
||||||
|
|
||||||
Warning::suppress(Warning::RESOLVE_TYPE_WARNING);
|
Warning::suppress(Warning::FULL_SCHEMA_SCAN_WARNING);
|
||||||
$this->assertEquals($expected, Executor::execute($this->schema, $ast, $this->john)->toArray());
|
$this->assertEquals($expected, Executor::execute($this->schema, $ast, $this->john)->toArray());
|
||||||
Warning::enable(Warning::RESOLVE_TYPE_WARNING);
|
Warning::enable(Warning::FULL_SCHEMA_SCAN_WARNING);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -351,9 +351,9 @@ class UnionInterfaceTest extends \PHPUnit_Framework_TestCase
|
|||||||
]
|
]
|
||||||
];
|
];
|
||||||
|
|
||||||
Warning::suppress(Warning::RESOLVE_TYPE_WARNING);
|
Warning::suppress(Warning::FULL_SCHEMA_SCAN_WARNING);
|
||||||
$this->assertEquals($expected, Executor::execute($this->schema, $ast, $this->john)->toArray());
|
$this->assertEquals($expected, Executor::execute($this->schema, $ast, $this->john)->toArray());
|
||||||
Warning::enable(Warning::RESOLVE_TYPE_WARNING);
|
Warning::enable(Warning::FULL_SCHEMA_SCAN_WARNING);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -235,6 +235,10 @@ class TypeLoaderTest extends \PHPUnit_Framework_TestCase
|
|||||||
$input = $schema->getType('PostStoryMutationInput');
|
$input = $schema->getType('PostStoryMutationInput');
|
||||||
$this->assertSame($this->postStoryMutationInput, $input);
|
$this->assertSame($this->postStoryMutationInput, $input);
|
||||||
$this->assertEquals(['Node', 'Content', 'PostStoryMutationInput'], $this->calls);
|
$this->assertEquals(['Node', 'Content', 'PostStoryMutationInput'], $this->calls);
|
||||||
|
|
||||||
|
$result = $schema->isPossibleType($this->node, $this->blogStory);
|
||||||
|
$this->assertTrue($result);
|
||||||
|
$this->assertEquals(['Node', 'Content', 'PostStoryMutationInput'], $this->calls);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testOnlyCallsLoaderOnce()
|
public function testOnlyCallsLoaderOnce()
|
||||||
@ -316,4 +320,23 @@ class TypeLoaderTest extends \PHPUnit_Framework_TestCase
|
|||||||
|
|
||||||
$schema->getType('Node');
|
$schema->getType('Node');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function testReturnsIdenticalResults()
|
||||||
|
{
|
||||||
|
$withoutLoader = new Schema([
|
||||||
|
'query' => $this->query,
|
||||||
|
'mutation' => $this->mutation
|
||||||
|
]);
|
||||||
|
|
||||||
|
$withLoader = new Schema([
|
||||||
|
'query' => $this->query,
|
||||||
|
'mutation' => $this->mutation,
|
||||||
|
'typeLoader' => $this->typeLoader
|
||||||
|
]);
|
||||||
|
|
||||||
|
$this->assertSame($withoutLoader->getQueryType(), $withLoader->getQueryType());
|
||||||
|
$this->assertSame($withoutLoader->getMutationType(), $withLoader->getMutationType());
|
||||||
|
$this->assertSame($withoutLoader->getType('BlogStory'), $withLoader->getType('BlogStory'));
|
||||||
|
$this->assertSame($withoutLoader->getDirectives(), $withLoader->getDirectives());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user