mirror of
https://github.com/retailcrm/graphql-php.git
synced 2024-11-22 04:46:04 +03:00
Ability for interface types to resolve type asynchronously
This commit is contained in:
parent
445f579f09
commit
d64c352262
@ -969,9 +969,59 @@ class Executor
|
||||
$runtimeType = $returnType->resolveType($result, $exeContext->contextValue, $info);
|
||||
|
||||
if (null === $runtimeType) {
|
||||
$runtimeType = self::inferTypeOf($result, $exeContext->contextValue, $info, $returnType);
|
||||
$runtimeType = self::defaultTypeResolver($result, $exeContext->contextValue, $info, $returnType);
|
||||
}
|
||||
|
||||
if ($this->promises->isThenable($runtimeType)) {
|
||||
$runtimeType = $this->promises->convertThenable($runtimeType);
|
||||
Utils::invariant($runtimeType instanceof Promise);
|
||||
}
|
||||
|
||||
if ($runtimeType instanceof Promise) {
|
||||
return $runtimeType->then(function($resolvedRuntimeType) use ($returnType, $fieldNodes, $info, $path, &$result) {
|
||||
return $this->validateRuntimeTypeAndCompleteObjectValue(
|
||||
$returnType,
|
||||
$fieldNodes,
|
||||
$info,
|
||||
$path,
|
||||
$resolvedRuntimeType,
|
||||
$result
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
return $this->validateRuntimeTypeAndCompleteObjectValue(
|
||||
$returnType,
|
||||
$fieldNodes,
|
||||
$info,
|
||||
$path,
|
||||
$runtimeType,
|
||||
$result
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param AbstractType $returnType
|
||||
* @param FieldNode[] $fieldNodes
|
||||
* @param ResolveInfo $info
|
||||
* @param array $path
|
||||
* @param mixed $returnedRuntimeType
|
||||
* @param $result
|
||||
* @return array|Promise|\stdClass
|
||||
* @throws Error
|
||||
*/
|
||||
private function validateRuntimeTypeAndCompleteObjectValue(
|
||||
AbstractType $returnType,
|
||||
$fieldNodes,
|
||||
ResolveInfo $info,
|
||||
$path,
|
||||
$returnedRuntimeType,
|
||||
&$result
|
||||
)
|
||||
{
|
||||
$exeContext = $this->exeContext;
|
||||
$runtimeType = $returnedRuntimeType;
|
||||
|
||||
// If resolveType returns a string, we assume it's a ObjectType name.
|
||||
if (is_string($runtimeType)) {
|
||||
$runtimeType = $exeContext->schema->getType($runtimeType);
|
||||
@ -1064,12 +1114,61 @@ class Executor
|
||||
*/
|
||||
private function completeObjectValue(ObjectType $returnType, $fieldNodes, ResolveInfo $info, $path, &$result)
|
||||
{
|
||||
$exeContext = $this->exeContext;
|
||||
$isTypeOf = $returnType->isTypeOf($result, $this->exeContext->contextValue, $info);
|
||||
|
||||
// If there is an isTypeOf predicate function, call it with the
|
||||
// current result. If isTypeOf returns false, then raise an error rather
|
||||
// than continuing execution.
|
||||
if (false === $returnType->isTypeOf($result, $exeContext->contextValue, $info)) {
|
||||
if (null === $isTypeOf) {
|
||||
return $this->validateResultTypeAndExecuteFields(
|
||||
$returnType,
|
||||
$fieldNodes,
|
||||
$info,
|
||||
$path,
|
||||
$result,
|
||||
true
|
||||
);
|
||||
}
|
||||
|
||||
if ($this->promises->isThenable($isTypeOf)) {
|
||||
/** @var Promise $isTypeOf */
|
||||
$isTypeOf = $this->promises->convertThenable($isTypeOf);
|
||||
Utils::invariant($isTypeOf instanceof Promise);
|
||||
|
||||
return $isTypeOf->then(function($isTypeOfResult) use ($returnType, $fieldNodes, $info, $path, &$result) {
|
||||
return $this->validateResultTypeAndExecuteFields(
|
||||
$returnType,
|
||||
$fieldNodes,
|
||||
$info,
|
||||
$path,
|
||||
$result,
|
||||
$isTypeOfResult
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
return $this->validateResultTypeAndExecuteFields($returnType, $fieldNodes, $info, $path, $result, $isTypeOf);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param ObjectType $returnType
|
||||
* @param FieldNode[] $fieldNodes
|
||||
* @param ResolveInfo $info
|
||||
* @param array $path
|
||||
* @param array $result
|
||||
* @param bool $isTypeOfResult
|
||||
* @return array|Promise|\stdClass
|
||||
* @throws Error
|
||||
*/
|
||||
private function validateResultTypeAndExecuteFields(
|
||||
ObjectType $returnType,
|
||||
$fieldNodes,
|
||||
ResolveInfo $info,
|
||||
$path,
|
||||
&$result,
|
||||
$isTypeOfResult
|
||||
)
|
||||
{
|
||||
// If isTypeOf returns false, then raise an error
|
||||
// rather than continuing execution.
|
||||
if (false === $isTypeOfResult) {
|
||||
throw new Error(
|
||||
"Expected value of type $returnType but got: " . Utils::getVariableType($result),
|
||||
$fieldNodes
|
||||
@ -1095,23 +1194,47 @@ class Executor
|
||||
}
|
||||
|
||||
/**
|
||||
* Infer type of the value using isTypeOf of corresponding AbstractType
|
||||
* If a resolveType function is not given, then a default resolve behavior is
|
||||
* used which tests each possible type for the abstract type by calling
|
||||
* isTypeOf for the object being coerced, returning the first type that matches.
|
||||
*
|
||||
* @param $value
|
||||
* @param $context
|
||||
* @param ResolveInfo $info
|
||||
* @param AbstractType $abstractType
|
||||
* @return ObjectType|null
|
||||
* @return ObjectType|Promise|null
|
||||
*/
|
||||
private static function inferTypeOf($value, $context, ResolveInfo $info, AbstractType $abstractType)
|
||||
private function defaultTypeResolver($value, $context, ResolveInfo $info, AbstractType $abstractType)
|
||||
{
|
||||
$possibleTypes = $info->schema->getPossibleTypes($abstractType);
|
||||
$promisedIsTypeOfResults = [];
|
||||
$promisedIsTypeOfResultTypes = [];
|
||||
|
||||
foreach ($possibleTypes as $type) {
|
||||
if ($type->isTypeOf($value, $context, $info)) {
|
||||
return $type;
|
||||
$isTypeOfResult = $type->isTypeOf($value, $context, $info);
|
||||
|
||||
if (null !== $isTypeOfResult) {
|
||||
if ($this->promises->isThenable($isTypeOfResult)) {
|
||||
$promisedIsTypeOfResults[] = $this->promises->convertThenable($isTypeOfResult);
|
||||
$promisedIsTypeOfResultTypes[] = $type;
|
||||
} else if ($isTypeOfResult) {
|
||||
return $type;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!empty($promisedIsTypeOfResults)) {
|
||||
return $this->promises->all($promisedIsTypeOfResults)
|
||||
->then(function($isTypeOfResults) use ($promisedIsTypeOfResultTypes) {
|
||||
foreach ($isTypeOfResults as $index => $result) {
|
||||
if ($result) {
|
||||
return $promisedIsTypeOfResultTypes[$index];
|
||||
}
|
||||
}
|
||||
return null;
|
||||
});
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
|
654
tests/Executor/AbstractPromiseTest.php
Normal file
654
tests/Executor/AbstractPromiseTest.php
Normal file
@ -0,0 +1,654 @@
|
||||
<?php
|
||||
namespace GraphQL\Tests\Executor;
|
||||
|
||||
use GraphQL\Deferred;
|
||||
use GraphQL\GraphQL;
|
||||
use GraphQL\Schema;
|
||||
use GraphQL\Type\Definition\InterfaceType;
|
||||
use GraphQL\Type\Definition\ObjectType;
|
||||
use GraphQL\Type\Definition\Type;
|
||||
use GraphQL\Type\Definition\UnionType;
|
||||
|
||||
require_once __DIR__ . '/TestClasses.php';
|
||||
|
||||
class AbstractPromiseTest extends \PHPUnit_Framework_TestCase
|
||||
{
|
||||
// DESCRIBE: Execute: Handles execution of abstract types with promises
|
||||
|
||||
/**
|
||||
* @it isTypeOf used to resolve runtime type for Interface
|
||||
*/
|
||||
public function testIsTypeOfUsedToResolveRuntimeTypeForInterface()
|
||||
{
|
||||
$PetType = new InterfaceType([
|
||||
'name' => 'Pet',
|
||||
'fields' => [
|
||||
'name' => [ 'type' => Type::string() ]
|
||||
]
|
||||
]);
|
||||
|
||||
$DogType = new ObjectType([
|
||||
'name' => 'Dog',
|
||||
'interfaces' => [ $PetType ],
|
||||
'isTypeOf' => function($obj) {
|
||||
return new Deferred(function() use ($obj) {
|
||||
return $obj instanceof Dog;
|
||||
});
|
||||
},
|
||||
'fields' => [
|
||||
'name' => [ 'type' => Type::string() ],
|
||||
'woofs' => [ 'type' => Type::boolean() ],
|
||||
]
|
||||
]);
|
||||
|
||||
$CatType = new ObjectType([
|
||||
'name' => 'Cat',
|
||||
'interfaces' => [ $PetType ],
|
||||
'isTypeOf' => function($obj) {
|
||||
return new Deferred(function() use ($obj) {
|
||||
return $obj instanceof Cat;
|
||||
});
|
||||
},
|
||||
'fields' => [
|
||||
'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 ]
|
||||
]);
|
||||
|
||||
$query = '{
|
||||
pets {
|
||||
name
|
||||
... on Dog {
|
||||
woofs
|
||||
}
|
||||
... on Cat {
|
||||
meows
|
||||
}
|
||||
}
|
||||
}';
|
||||
|
||||
$result = GraphQL::execute($schema, $query);
|
||||
|
||||
$expected = [
|
||||
'data' => [
|
||||
'pets' => [
|
||||
[ 'name' => 'Odie', 'woofs' => true ],
|
||||
[ 'name' => 'Garfield', 'meows' => false ]
|
||||
]
|
||||
]
|
||||
];
|
||||
|
||||
$this->assertEquals($expected, $result);
|
||||
}
|
||||
|
||||
/**
|
||||
* @it isTypeOf can be rejected
|
||||
*/
|
||||
public function testIsTypeOfCanBeRejected()
|
||||
{
|
||||
|
||||
$PetType = new InterfaceType([
|
||||
'name' => 'Pet',
|
||||
'fields' => [
|
||||
'name' => ['type' => Type::string()]
|
||||
]
|
||||
]);
|
||||
|
||||
$DogType = new ObjectType([
|
||||
'name' => 'Dog',
|
||||
'interfaces' => [$PetType],
|
||||
'isTypeOf' => function () {
|
||||
return new Deferred(function () {
|
||||
throw new \Exception('We are testing this error');
|
||||
});
|
||||
},
|
||||
'fields' => [
|
||||
'name' => ['type' => Type::string()],
|
||||
'woofs' => ['type' => Type::boolean()],
|
||||
]
|
||||
]);
|
||||
|
||||
$CatType = new ObjectType([
|
||||
'name' => 'Cat',
|
||||
'interfaces' => [$PetType],
|
||||
'isTypeOf' => function ($obj) {
|
||||
return new Deferred(function () use ($obj) {
|
||||
return $obj instanceof Cat;
|
||||
});
|
||||
},
|
||||
'fields' => [
|
||||
'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]
|
||||
]);
|
||||
|
||||
$query = '{
|
||||
pets {
|
||||
name
|
||||
... on Dog {
|
||||
woofs
|
||||
}
|
||||
... on Cat {
|
||||
meows
|
||||
}
|
||||
}
|
||||
}';
|
||||
|
||||
$result = GraphQL::execute($schema, $query);
|
||||
|
||||
$expected = [
|
||||
'data' => [
|
||||
'pets' => [null, null]
|
||||
],
|
||||
'errors' => [
|
||||
[
|
||||
'message' => 'We are testing this error',
|
||||
'locations' => [['line' => 2, 'column' => 7]],
|
||||
'path' => ['pets', 0]
|
||||
],
|
||||
[
|
||||
'message' => 'We are testing this error',
|
||||
'locations' => [['line' => 2, 'column' => 7]],
|
||||
'path' => ['pets', 1]
|
||||
]
|
||||
]
|
||||
];
|
||||
|
||||
$this->assertEquals($expected, $result);
|
||||
}
|
||||
|
||||
/**
|
||||
* @it isTypeOf used to resolve runtime type for Union
|
||||
*/
|
||||
public function testIsTypeOfUsedToResolveRuntimeTypeForUnion()
|
||||
{
|
||||
|
||||
$DogType = new ObjectType([
|
||||
'name' => 'Dog',
|
||||
'isTypeOf' => function ($obj) {
|
||||
return new Deferred(function () use ($obj) {
|
||||
return $obj instanceof Dog;
|
||||
});
|
||||
},
|
||||
'fields' => [
|
||||
'name' => ['type' => Type::string()],
|
||||
'woofs' => ['type' => Type::boolean()],
|
||||
]
|
||||
]);
|
||||
|
||||
$CatType = new ObjectType([
|
||||
'name' => 'Cat',
|
||||
'isTypeOf' => function ($obj) {
|
||||
return new Deferred(function () use ($obj) {
|
||||
return $obj instanceof Cat;
|
||||
});
|
||||
},
|
||||
'fields' => [
|
||||
'name' => ['type' => Type::string()],
|
||||
'meows' => ['type' => Type::boolean()],
|
||||
]
|
||||
]);
|
||||
|
||||
$PetType = new UnionType([
|
||||
'name' => 'Pet',
|
||||
'types' => [$DogType, $CatType]
|
||||
]);
|
||||
|
||||
$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)];
|
||||
}
|
||||
]
|
||||
]
|
||||
])
|
||||
]);
|
||||
|
||||
$query = '{
|
||||
pets {
|
||||
... on Dog {
|
||||
name
|
||||
woofs
|
||||
}
|
||||
... on Cat {
|
||||
name
|
||||
meows
|
||||
}
|
||||
}
|
||||
}';
|
||||
|
||||
$result = GraphQL::execute($schema, $query);
|
||||
|
||||
$expected = [
|
||||
'data' => [
|
||||
'pets' => [
|
||||
['name' => 'Odie', 'woofs' => true],
|
||||
['name' => 'Garfield', 'meows' => false]
|
||||
]
|
||||
]
|
||||
];
|
||||
|
||||
$this->assertEquals($expected, $result);
|
||||
}
|
||||
|
||||
/**
|
||||
* @it resolveType on Interface yields useful error
|
||||
*/
|
||||
public function testResolveTypeOnInterfaceYieldsUsefulError()
|
||||
{
|
||||
$PetType = new InterfaceType([
|
||||
'name' => 'Pet',
|
||||
'resolveType' => function ($obj) use (&$DogType, &$CatType, &$HumanType) {
|
||||
return new Deferred(function () use ($obj, $DogType, $CatType, $HumanType) {
|
||||
if ($obj instanceof Dog) {
|
||||
return $DogType;
|
||||
}
|
||||
if ($obj instanceof Cat) {
|
||||
return $CatType;
|
||||
}
|
||||
if ($obj instanceof Human) {
|
||||
return $HumanType;
|
||||
}
|
||||
return null;
|
||||
});
|
||||
},
|
||||
'fields' => [
|
||||
'name' => ['type' => Type::string()]
|
||||
]
|
||||
]);
|
||||
|
||||
$HumanType = new ObjectType([
|
||||
'name' => 'Human',
|
||||
'fields' => [
|
||||
'name' => ['type' => Type::string()],
|
||||
]
|
||||
]);
|
||||
|
||||
$DogType = new ObjectType([
|
||||
'name' => 'Dog',
|
||||
'interfaces' => [$PetType],
|
||||
'fields' => [
|
||||
'name' => ['type' => Type::string()],
|
||||
'woofs' => ['type' => Type::boolean()],
|
||||
]
|
||||
]);
|
||||
|
||||
$CatType = new ObjectType([
|
||||
'name' => 'Cat',
|
||||
'interfaces' => [$PetType],
|
||||
'fields' => [
|
||||
'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 Deferred(function () {
|
||||
return [
|
||||
new Dog('Odie', true),
|
||||
new Cat('Garfield', false),
|
||||
new Human('Jon')
|
||||
];
|
||||
});
|
||||
}
|
||||
]
|
||||
]
|
||||
]),
|
||||
'types' => [$CatType, $DogType]
|
||||
]);
|
||||
|
||||
$query = '{
|
||||
pets {
|
||||
name
|
||||
... on Dog {
|
||||
woofs
|
||||
}
|
||||
... on Cat {
|
||||
meows
|
||||
}
|
||||
}
|
||||
}';
|
||||
|
||||
$result = GraphQL::execute($schema, $query);
|
||||
|
||||
$expected = [
|
||||
'data' => [
|
||||
'pets' => [
|
||||
['name' => 'Odie', 'woofs' => true],
|
||||
['name' => 'Garfield', 'meows' => false],
|
||||
null
|
||||
]
|
||||
],
|
||||
'errors' => [
|
||||
[
|
||||
'message' => 'Runtime Object type "Human" is not a possible type for "Pet".',
|
||||
'locations' => [['line' => 2, 'column' => 7]],
|
||||
'path' => ['pets', 2]
|
||||
],
|
||||
]
|
||||
];
|
||||
|
||||
$this->assertEquals($expected, $result);
|
||||
}
|
||||
|
||||
/**
|
||||
* @it resolveType on Union yields useful error
|
||||
*/
|
||||
public function testResolveTypeOnUnionYieldsUsefulError()
|
||||
{
|
||||
|
||||
$HumanType = new ObjectType([
|
||||
'name' => 'Human',
|
||||
'fields' => [
|
||||
'name' => ['type' => Type::string()],
|
||||
]
|
||||
]);
|
||||
|
||||
$DogType = new ObjectType([
|
||||
'name' => 'Dog',
|
||||
'fields' => [
|
||||
'name' => ['type' => Type::string()],
|
||||
'woofs' => ['type' => Type::boolean()],
|
||||
]
|
||||
]);
|
||||
|
||||
$CatType = new ObjectType([
|
||||
'name' => 'Cat',
|
||||
'fields' => [
|
||||
'name' => ['type' => Type::string()],
|
||||
'meows' => ['type' => Type::boolean()],
|
||||
]
|
||||
]);
|
||||
|
||||
$PetType = new UnionType([
|
||||
'name' => 'Pet',
|
||||
'resolveType' => function ($obj) use ($DogType, $CatType, $HumanType) {
|
||||
return new Deferred(function () use ($obj, $DogType, $CatType, $HumanType) {
|
||||
if ($obj instanceof Dog) {
|
||||
return $DogType;
|
||||
}
|
||||
if ($obj instanceof Cat) {
|
||||
return $CatType;
|
||||
}
|
||||
if ($obj instanceof Human) {
|
||||
return $HumanType;
|
||||
}
|
||||
return null;
|
||||
});
|
||||
},
|
||||
'types' => [$DogType, $CatType]
|
||||
]);
|
||||
|
||||
$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),
|
||||
new Human('Jon')
|
||||
];
|
||||
}
|
||||
]
|
||||
]
|
||||
])
|
||||
]);
|
||||
|
||||
$query = '{
|
||||
pets {
|
||||
... on Dog {
|
||||
name
|
||||
woofs
|
||||
}
|
||||
... on Cat {
|
||||
name
|
||||
meows
|
||||
}
|
||||
}
|
||||
}';
|
||||
|
||||
$result = GraphQL::execute($schema, $query);
|
||||
|
||||
$expected = [
|
||||
'data' => [
|
||||
'pets' => [
|
||||
['name' => 'Odie', 'woofs' => true],
|
||||
['name' => 'Garfield', 'meows' => false],
|
||||
null
|
||||
]
|
||||
],
|
||||
'errors' => [
|
||||
[
|
||||
'message' => 'Runtime Object type "Human" is not a possible type for "Pet".',
|
||||
'locations' => [['line' => 2, 'column' => 7]],
|
||||
'path' => ['pets', 2]
|
||||
]
|
||||
]
|
||||
];
|
||||
|
||||
$this->assertEquals($expected, $result);
|
||||
}
|
||||
|
||||
/**
|
||||
* @it resolveType allows resolving with type name
|
||||
*/
|
||||
public function testResolveTypeAllowsResolvingWithTypeName()
|
||||
{
|
||||
$PetType = new InterfaceType([
|
||||
'name' => 'Pet',
|
||||
'resolveType' => function ($obj) {
|
||||
return new Deferred(function () use ($obj) {
|
||||
if ($obj instanceof Dog) {
|
||||
return 'Dog';
|
||||
}
|
||||
if ($obj instanceof Cat) {
|
||||
return 'Cat';
|
||||
}
|
||||
return null;
|
||||
});
|
||||
},
|
||||
'fields' => [
|
||||
'name' => ['type' => Type::string()]
|
||||
]
|
||||
]);
|
||||
|
||||
|
||||
$DogType = new ObjectType([
|
||||
'name' => 'Dog',
|
||||
'interfaces' => [$PetType],
|
||||
'fields' => [
|
||||
'name' => ['type' => Type::string()],
|
||||
'woofs' => ['type' => Type::boolean()],
|
||||
]
|
||||
]);
|
||||
|
||||
$CatType = new ObjectType([
|
||||
'name' => 'Cat',
|
||||
'interfaces' => [$PetType],
|
||||
'fields' => [
|
||||
'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]
|
||||
]);
|
||||
|
||||
$query = '{
|
||||
pets {
|
||||
name
|
||||
... on Dog {
|
||||
woofs
|
||||
}
|
||||
... on Cat {
|
||||
meows
|
||||
}
|
||||
}
|
||||
}';
|
||||
|
||||
$result = GraphQL::execute($schema, $query);
|
||||
|
||||
$expected = [
|
||||
'data' => [
|
||||
'pets' => [
|
||||
['name' => 'Odie', 'woofs' => true],
|
||||
['name' => 'Garfield', 'meows' => false],
|
||||
]
|
||||
]
|
||||
];
|
||||
$this->assertEquals($expected, $result);
|
||||
}
|
||||
|
||||
/**
|
||||
* @it resolveType can be caught
|
||||
*/
|
||||
public function testResolveTypeCanBeCaught()
|
||||
{
|
||||
|
||||
$PetType = new InterfaceType([
|
||||
'name' => 'Pet',
|
||||
'resolveType' => function () {
|
||||
return new Deferred(function () {
|
||||
throw new \Exception('We are testing this error');
|
||||
});
|
||||
},
|
||||
'fields' => [
|
||||
'name' => ['type' => Type::string()]
|
||||
]
|
||||
]);
|
||||
|
||||
$DogType = new ObjectType([
|
||||
'name' => 'Dog',
|
||||
'interfaces' => [$PetType],
|
||||
'fields' => [
|
||||
'name' => ['type' => Type::string()],
|
||||
'woofs' => ['type' => Type::boolean()],
|
||||
]
|
||||
]);
|
||||
|
||||
$CatType = new ObjectType([
|
||||
'name' => 'Cat',
|
||||
'interfaces' => [$PetType],
|
||||
'fields' => [
|
||||
'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]
|
||||
]);
|
||||
|
||||
$query = '{
|
||||
pets {
|
||||
name
|
||||
... on Dog {
|
||||
woofs
|
||||
}
|
||||
... on Cat {
|
||||
meows
|
||||
}
|
||||
}
|
||||
}';
|
||||
|
||||
$result = GraphQL::execute($schema, $query);
|
||||
|
||||
$expected = [
|
||||
'data' => [
|
||||
'pets' => [null, null]
|
||||
],
|
||||
'errors' => [
|
||||
[
|
||||
'message' => 'We are testing this error',
|
||||
'locations' => [['line' => 2, 'column' => 7]],
|
||||
'path' => ['pets', 0]
|
||||
],
|
||||
[
|
||||
'message' => 'We are testing this error',
|
||||
'locations' => [['line' => 2, 'column' => 7]],
|
||||
'path' => ['pets', 1]
|
||||
]
|
||||
]
|
||||
];
|
||||
|
||||
$this->assertEquals($expected, $result);
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user