From 64c463e8899802e947da4a9349c1ca47b8b10f90 Mon Sep 17 00:00:00 2001 From: Vladimir Razuvaev Date: Wed, 8 Aug 2018 15:47:42 +0700 Subject: [PATCH] Schema: getTypeMap() should include input types defined in directive arguments --- src/Type/Definition/Directive.php | 12 +++ src/Type/Schema.php | 5 ++ src/Utils/TypeInfo.php | 15 ++++ tests/Type/SchemaTest.php | 120 ++++++++++++++++++++++++++++++ 4 files changed, 152 insertions(+) create mode 100644 tests/Type/SchemaTest.php diff --git a/src/Type/Definition/Directive.php b/src/Type/Definition/Directive.php index b3b9a1a..b031365 100644 --- a/src/Type/Definition/Directive.php +++ b/src/Type/Definition/Directive.php @@ -157,6 +157,18 @@ class Directive */ 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) { $this->{$key} = $value; } diff --git a/src/Type/Schema.php b/src/Type/Schema.php index 80fa8ab..c39d374 100644 --- a/src/Type/Schema.php +++ b/src/Type/Schema.php @@ -227,6 +227,11 @@ class Schema foreach ($this->resolvedTypes as $type) { $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 if (is_callable($this->config->types)) { foreach ($this->resolveAdditionalTypes() as $type) { diff --git a/src/Utils/TypeInfo.php b/src/Utils/TypeInfo.php index a211c5a..a1773a0 100644 --- a/src/Utils/TypeInfo.php +++ b/src/Utils/TypeInfo.php @@ -140,6 +140,21 @@ class TypeInfo 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 * statically evaluated environment we do not always have an Object type, diff --git a/tests/Type/SchemaTest.php b/tests/Type/SchemaTest.php new file mode 100644 index 0000000..1e7a56c --- /dev/null +++ b/tests/Type/SchemaTest.php @@ -0,0 +1,120 @@ +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); + } +}