Schema: getTypeMap() should include input types defined in directive arguments

This commit is contained in:
Vladimir Razuvaev 2018-08-08 15:47:42 +07:00
parent 0d93d190f8
commit 64c463e889
4 changed files with 152 additions and 0 deletions

View File

@ -157,6 +157,18 @@ class Directive
*/ */
public function __construct(array $config) public function __construct(array $config)
{ {
if (isset($config['args'])) {
$args = [];
foreach ($config['args'] as $name => $arg) {
if (is_array($arg)) {
$args[] = FieldDefinition::create($arg + ['name' => $name]);
} else {
$args[] = $arg;
}
}
$this->args = $args;
unset($config['args']);
}
foreach ($config as $key => $value) { foreach ($config as $key => $value) {
$this->{$key} = $value; $this->{$key} = $value;
} }

View File

@ -227,6 +227,11 @@ class Schema
foreach ($this->resolvedTypes as $type) { foreach ($this->resolvedTypes as $type) {
$typeMap = TypeInfo::extractTypes($type, $typeMap); $typeMap = TypeInfo::extractTypes($type, $typeMap);
} }
foreach ($this->getDirectives() as $directive) {
if ($directive instanceof Directive) {
$typeMap = TypeInfo::extractTypesFromDirectives($directive, $typeMap);
}
}
// When types are set as array they are resolved in constructor // When types are set as array they are resolved in constructor
if (is_callable($this->config->types)) { if (is_callable($this->config->types)) {
foreach ($this->resolveAdditionalTypes() as $type) { foreach ($this->resolveAdditionalTypes() as $type) {

View File

@ -140,6 +140,21 @@ class TypeInfo
return $typeMap; return $typeMap;
} }
/**
* @param Directive $directive
* @param array $typeMap
* @return array
*/
public static function extractTypesFromDirectives(Directive $directive, array $typeMap = [])
{
if (is_array($directive->args)) {
foreach ($directive->args as $arg) {
$typeMap = self::extractTypes($arg->getType(), $typeMap);
}
}
return $typeMap;
}
/** /**
* Not exactly the same as the executor's definition of getFieldDef, in this * Not exactly the same as the executor's definition of getFieldDef, in this
* statically evaluated environment we do not always have an Object type, * statically evaluated environment we do not always have an Object type,

120
tests/Type/SchemaTest.php Normal file
View File

@ -0,0 +1,120 @@
<?php
namespace GraphQL\Tests\Type;
use GraphQL\Error\InvariantViolation;
use GraphQL\Type\Definition\Directive;
use GraphQL\Type\Definition\InputObjectType;
use GraphQL\Type\Definition\InterfaceType;
use GraphQL\Type\Definition\ObjectType;
use GraphQL\Type\Definition\Type;
use GraphQL\Type\Schema;
use PHPUnit\Framework\TestCase;
class SchemaTest extends TestCase
{
private $interfaceType;
private $implementingType;
private $directiveInputType;
private $wrappedDirectiveInputType;
private $directive;
/** @var Schema */
private $schema;
public function setUp()
{
$this->interfaceType = new InterfaceType([
'name' => 'Interface',
'fields' => ['fieldName' => ['type' => Type::string()]],
]);
$this->implementingType = new ObjectType([
'name' => 'Object',
'interfaces' => [$this->interfaceType],
'fields' => ['fieldName' => ['type' => Type::string(), 'resolve' => function () {
return '';
}]],
]);
$this->directiveInputType = new InputObjectType([
'name' => 'DirInput',
'fields' => [
'field' => [
'type' => Type::string(),
],
],
]);
$this->wrappedDirectiveInputType = new InputObjectType([
'name' => 'WrappedDirInput',
'fields' => [
'field' => [
'type' => Type::string(),
],
],
]);
$this->directive = new Directive([
'name' => 'dir',
'locations' => ['OBJECT'],
'args' => [
'arg' => [
'type' => $this->directiveInputType,
],
'argList' => [
'type' => Type::listOf($this->wrappedDirectiveInputType),
],
],
]);
$this->schema = new Schema([
'query' => new ObjectType([
'name' => 'Query',
'fields' => [
'getObject' => [
'type' => $this->interfaceType,
'resolve' => function () {
return [];
},
],
],
]),
'directives' => [$this->directive],
]);
}
// Type System: Schema
// Getting possible types
/**
* @it throws human-reable error if schema.types is not defined
*/
public function testThrowsHumanReableErrorIfSchemaTypesIsNotDefined()
{
$this->markTestSkipped("Can't check interface implementations without full schema scan");
$this->expectException(InvariantViolation::class);
$this->expectExceptionMessage(
'Could not find possible implementing types for Interface in schema. ' .
'Check that schema.types is defined and is an array of all possible ' .
'types in the schema.'
);
$this->schema->isPossibleType($this->interfaceType, $this->implementingType);
}
// Type Map
/**
* @it includes input types only used in directives
*/
public function testIncludesInputTypesOnlyUsedInDirectives()
{
$typeMap = $this->schema->getTypeMap();
$this->assertArrayHasKey('DirInput', $typeMap);
$this->assertArrayHasKey('WrappedDirInput', $typeMap);
}
}