mirror of
https://github.com/retailcrm/graphql-php.git
synced 2024-11-25 06:16:05 +03:00
Default error formatter now returns "Internal server error" unless error is client-aware and safe to report directly to end-users
This commit is contained in:
parent
fbcd20814a
commit
38922dbbed
48
UPGRADE.md
48
UPGRADE.md
@ -1,15 +1,43 @@
|
|||||||
# Upgrade
|
# Upgrade
|
||||||
|
|
||||||
## Upgrade v0.9.x > v0.10.x
|
## Upgrade v0.9.x > v0.10.x
|
||||||
### Deprecated: GraphQL\Utils moved to GraphQL\Utils\Utils
|
#### Breaking: default error formatting
|
||||||
Old class still exists, but triggers deprecation warning.
|
By default exceptions thrown in resolvers will be reported with generic message `"Internal server error"`.
|
||||||
|
Only exceptions implementing interface `GraphQL\Error\ClientAware` and claiming themselves as `safe` will
|
||||||
|
be reported with full error message.
|
||||||
|
|
||||||
|
This is done to avoid information leak in production when unhandled exceptions occur in resolvers
|
||||||
|
(e.g. database connection errors, file access errors, etc).
|
||||||
|
|
||||||
|
During development or debugging use `$executionResult->toArray(true)`. It will add `debugMessage` key to
|
||||||
|
each error entry in result. If you also want to add `trace` for each error - pass flags instead:
|
||||||
|
|
||||||
|
```
|
||||||
|
use GraphQL\Error\FormattedError;
|
||||||
|
$debug = FormattedError::INCLUDE_DEBUG_MESSAGE | FormattedError::INCLUDE_TRACE;
|
||||||
|
$result = GraphQL::executeAndReturnResult()->toArray($debug);
|
||||||
|
```
|
||||||
|
|
||||||
|
To change default `"Internal server error"` message to something else, use:
|
||||||
|
```
|
||||||
|
GraphQL\Error\FormattedError::setInternalErrorMessage("Unexpected error");
|
||||||
|
```
|
||||||
|
|
||||||
|
**This change only affects default error reporting mechanism. If you set your own error formatter using
|
||||||
|
`ExecutionResult::setErrorFormatter()` you won't be affected by this change.**
|
||||||
|
|
||||||
|
If you are not happy with this change just set [your own error
|
||||||
|
formatter](http://webonyx.github.io/graphql-php/error-handling/#custom-error-formatting).
|
||||||
|
|
||||||
|
#### Deprecated: GraphQL\Utils moved to GraphQL\Utils\Utils
|
||||||
|
Old class still exists, but triggers deprecation warning when referenced.
|
||||||
|
|
||||||
## Upgrade v0.7.x > v0.8.x
|
## Upgrade v0.7.x > v0.8.x
|
||||||
All of those changes apply to those who extends various parts of this library.
|
All of those changes apply to those who extends various parts of this library.
|
||||||
If you only use the library and don't try to extend it - everything should work without breaks.
|
If you only use the library and don't try to extend it - everything should work without breaks.
|
||||||
|
|
||||||
|
|
||||||
### Breaking: Custom directives handling
|
#### Breaking: Custom directives handling
|
||||||
When passing custom directives to schema, default directives (like `@skip` and `@include`)
|
When passing custom directives to schema, default directives (like `@skip` and `@include`)
|
||||||
are not added to schema automatically anymore. If you need them - add them explicitly with
|
are not added to schema automatically anymore. If you need them - add them explicitly with
|
||||||
your other directives.
|
your other directives.
|
||||||
@ -30,15 +58,15 @@ $schema = new Schema([
|
|||||||
]);
|
]);
|
||||||
```
|
```
|
||||||
|
|
||||||
### Breaking: Schema protected property and methods visibility
|
#### Breaking: Schema protected property and methods visibility
|
||||||
Most of the `protected` properties and methods of `GraphQL\Schema` were changed to `private`.
|
Most of the `protected` properties and methods of `GraphQL\Schema` were changed to `private`.
|
||||||
Please use public interface instead.
|
Please use public interface instead.
|
||||||
|
|
||||||
### Breaking: Node kind constants
|
#### Breaking: Node kind constants
|
||||||
Node kind constants were extracted from `GraphQL\Language\AST\Node` to
|
Node kind constants were extracted from `GraphQL\Language\AST\Node` to
|
||||||
separate class `GraphQL\Language\AST\NodeKind`
|
separate class `GraphQL\Language\AST\NodeKind`
|
||||||
|
|
||||||
### Non-breaking: AST node classes renamed
|
#### Non-breaking: AST node classes renamed
|
||||||
AST node classes were renamed to disambiguate with types. e.g.:
|
AST node classes were renamed to disambiguate with types. e.g.:
|
||||||
|
|
||||||
```
|
```
|
||||||
@ -50,7 +78,7 @@ etc.
|
|||||||
Old names are still available via `class_alias` defined in `src/deprecated.php`.
|
Old names are still available via `class_alias` defined in `src/deprecated.php`.
|
||||||
This file is included automatically when using composer autoloading.
|
This file is included automatically when using composer autoloading.
|
||||||
|
|
||||||
### Deprecations
|
#### Deprecations
|
||||||
There are several deprecations which still work, but trigger `E_USER_DEPRECATED` when used.
|
There are several deprecations which still work, but trigger `E_USER_DEPRECATED` when used.
|
||||||
|
|
||||||
For example `GraphQL\Executor\Executor::setDefaultResolveFn()` is renamed to `setDefaultResolver()`
|
For example `GraphQL\Executor\Executor::setDefaultResolveFn()` is renamed to `setDefaultResolver()`
|
||||||
@ -61,7 +89,7 @@ but still works with old name.
|
|||||||
There are a few new breaking changes in v0.7.0 that were added to the graphql-js reference implementation
|
There are a few new breaking changes in v0.7.0 that were added to the graphql-js reference implementation
|
||||||
with the spec of April2016
|
with the spec of April2016
|
||||||
|
|
||||||
### 1. Context for resolver
|
#### 1. Context for resolver
|
||||||
|
|
||||||
You can now pass a custom context to the `GraphQL::execute` function that is available in all resolvers as 3rd argument.
|
You can now pass a custom context to the `GraphQL::execute` function that is available in all resolvers as 3rd argument.
|
||||||
This can for example be used to pass the current user etc.
|
This can for example be used to pass the current user etc.
|
||||||
@ -119,7 +147,7 @@ function resolveMyField($object, array $args, $context, ResolveInfo $info){
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
### 2. Schema constructor signature
|
#### 2. Schema constructor signature
|
||||||
|
|
||||||
The signature of the Schema constructor now accepts an associative config array instead of positional arguments:
|
The signature of the Schema constructor now accepts an associative config array instead of positional arguments:
|
||||||
|
|
||||||
@ -137,7 +165,7 @@ $schema = new Schema([
|
|||||||
]);
|
]);
|
||||||
```
|
```
|
||||||
|
|
||||||
### 3. Types can be directly passed to schema
|
#### 3. Types can be directly passed to schema
|
||||||
|
|
||||||
There are edge cases when GraphQL cannot infer some types from your schema.
|
There are edge cases when GraphQL cannot infer some types from your schema.
|
||||||
One example is when you define a field of interface type and object types implementing
|
One example is when you define a field of interface type and object types implementing
|
||||||
|
@ -113,6 +113,28 @@ exceptions thrown by resolvers. To access original exceptions use `$error->getPr
|
|||||||
But note that previous exception is only available for **Execution** errors and will be `null`
|
But note that previous exception is only available for **Execution** errors and will be `null`
|
||||||
for **Syntax** or **Validation** errors.
|
for **Syntax** or **Validation** errors.
|
||||||
|
|
||||||
|
For example:
|
||||||
|
|
||||||
|
```php
|
||||||
|
$result = GraphQL::executeAndReturnResult()
|
||||||
|
->setErrorFormatter(function(GraphQL\Error\Error $err) {
|
||||||
|
$resolverException = $err->getPrevious();
|
||||||
|
|
||||||
|
if ($resolverException instanceof MyResolverException) {
|
||||||
|
$formattedError = [
|
||||||
|
'message' => $resolverException->getMessage(),
|
||||||
|
'code' => $resolverException->getCode()
|
||||||
|
];
|
||||||
|
} else {
|
||||||
|
$formattedError = [
|
||||||
|
'message' => $err->getMessage()
|
||||||
|
];
|
||||||
|
}
|
||||||
|
return $formattedError;
|
||||||
|
})
|
||||||
|
->toArray();
|
||||||
|
```
|
||||||
|
|
||||||
# Schema Errors
|
# Schema Errors
|
||||||
So far we only covered errors which occur during query execution process. But schema definition can
|
So far we only covered errors which occur during query execution process. But schema definition can
|
||||||
also throw if there is an error in one of type definitions.
|
also throw if there is an error in one of type definitions.
|
||||||
|
17
src/Error/ClientAware.php
Normal file
17
src/Error/ClientAware.php
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
<?php
|
||||||
|
namespace GraphQL\Error;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Interface ClientAware
|
||||||
|
*
|
||||||
|
* @package GraphQL\Error
|
||||||
|
*/
|
||||||
|
interface ClientAware
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Returns true when exception message is safe to be displayed to client
|
||||||
|
*
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
public function isClientSafe();
|
||||||
|
}
|
@ -14,7 +14,7 @@ use GraphQL\Utils\Utils;
|
|||||||
*
|
*
|
||||||
* @package GraphQL
|
* @package GraphQL
|
||||||
*/
|
*/
|
||||||
class Error extends \Exception implements \JsonSerializable
|
class Error extends \Exception implements \JsonSerializable, ClientAware
|
||||||
{
|
{
|
||||||
/**
|
/**
|
||||||
* A message describing the Error for debugging purposes.
|
* A message describing the Error for debugging purposes.
|
||||||
@ -62,6 +62,11 @@ class Error extends \Exception implements \JsonSerializable
|
|||||||
*/
|
*/
|
||||||
private $positions;
|
private $positions;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var bool
|
||||||
|
*/
|
||||||
|
private $isClientSafe;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Given an arbitrary Error, presumably thrown while attempting to execute a
|
* Given an arbitrary Error, presumably thrown while attempting to execute a
|
||||||
* GraphQL operation, produce a new GraphQLError aware of the location in the
|
* GraphQL operation, produce a new GraphQLError aware of the location in the
|
||||||
@ -133,6 +138,22 @@ class Error extends \Exception implements \JsonSerializable
|
|||||||
$this->source = $source;
|
$this->source = $source;
|
||||||
$this->positions = $positions;
|
$this->positions = $positions;
|
||||||
$this->path = $path;
|
$this->path = $path;
|
||||||
|
|
||||||
|
if ($previous instanceof ClientAware) {
|
||||||
|
$this->isClientSafe = $previous->isClientSafe();
|
||||||
|
} else if ($previous === null) {
|
||||||
|
$this->isClientSafe = true;
|
||||||
|
} else {
|
||||||
|
$this->isClientSafe = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
public function isClientSafe()
|
||||||
|
{
|
||||||
|
return $this->isClientSafe;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -190,6 +211,7 @@ class Error extends \Exception implements \JsonSerializable
|
|||||||
/**
|
/**
|
||||||
* Returns array representation of error suitable for serialization
|
* Returns array representation of error suitable for serialization
|
||||||
*
|
*
|
||||||
|
* @deprecated Use FormattedError::createFromException() instead
|
||||||
* @return array
|
* @return array
|
||||||
*/
|
*/
|
||||||
public function toSerializableArray()
|
public function toSerializableArray()
|
||||||
|
@ -13,20 +13,67 @@ use GraphQL\Utils\Utils;
|
|||||||
*/
|
*/
|
||||||
class FormattedError
|
class FormattedError
|
||||||
{
|
{
|
||||||
|
const INCLUDE_DEBUG_MESSAGE = 1;
|
||||||
|
const INCLUDE_TRACE = 2;
|
||||||
|
|
||||||
|
private static $internalErrorMessage = 'Internal server error';
|
||||||
|
|
||||||
|
public static function setInternalErrorMessage($msg)
|
||||||
|
{
|
||||||
|
self::$internalErrorMessage = $msg;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param \Throwable $e
|
* @param \Throwable $e
|
||||||
* @param $debug
|
* @param bool|int $debug
|
||||||
|
* @param string $internalErrorMessage
|
||||||
*
|
*
|
||||||
* @return array
|
* @return array
|
||||||
*/
|
*/
|
||||||
public static function createFromException($e, $debug = false)
|
public static function createFromException($e, $debug = false, $internalErrorMessage = null)
|
||||||
{
|
{
|
||||||
if ($e instanceof Error) {
|
Utils::invariant(
|
||||||
$result = $e->toSerializableArray();
|
$e instanceof \Exception || $e instanceof \Throwable,
|
||||||
} else if ($e instanceof \ErrorException) {
|
"Expected exception, got %s",
|
||||||
|
Utils::getVariableType($e)
|
||||||
|
);
|
||||||
|
|
||||||
|
$debug = (int) $debug;
|
||||||
|
$internalErrorMessage = $internalErrorMessage ?: self::$internalErrorMessage;
|
||||||
|
|
||||||
|
if ($e instanceof ClientAware) {
|
||||||
|
if ($e->isClientSafe()) {
|
||||||
|
$result = [
|
||||||
|
'message' => $e->getMessage()
|
||||||
|
];
|
||||||
|
} else {
|
||||||
|
$result = [
|
||||||
|
'message' => $internalErrorMessage,
|
||||||
|
'isInternalError' => true
|
||||||
|
];
|
||||||
|
}
|
||||||
|
} else {
|
||||||
$result = [
|
$result = [
|
||||||
'message' => $e->getMessage(),
|
'message' => $internalErrorMessage,
|
||||||
|
'isInternalError' => true
|
||||||
];
|
];
|
||||||
|
}
|
||||||
|
if (($debug & self::INCLUDE_DEBUG_MESSAGE > 0) && $result['message'] === $internalErrorMessage) {
|
||||||
|
$result['debugMessage'] = $e->getMessage();
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($e instanceof Error) {
|
||||||
|
$locations = Utils::map($e->getLocations(), function(SourceLocation $loc) {
|
||||||
|
return $loc->toSerializableArray();
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!empty($locations)) {
|
||||||
|
$result['locations'] = $locations;
|
||||||
|
}
|
||||||
|
if (!empty($e->path)) {
|
||||||
|
$result['path'] = $e->path;
|
||||||
|
}
|
||||||
|
} else if ($e instanceof \ErrorException) {
|
||||||
if ($debug) {
|
if ($debug) {
|
||||||
$result += [
|
$result += [
|
||||||
'file' => $e->getFile(),
|
'file' => $e->getFile(),
|
||||||
@ -34,18 +81,16 @@ class FormattedError
|
|||||||
'severity' => $e->getSeverity()
|
'severity' => $e->getSeverity()
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
} else {
|
} else if ($e instanceof \Error) {
|
||||||
Utils::invariant(
|
if ($debug) {
|
||||||
$e instanceof \Exception || $e instanceof \Throwable,
|
$result += [
|
||||||
"Expected exception, got %s",
|
'file' => $e->getFile(),
|
||||||
Utils::getVariableType($e)
|
'line' => $e->getLine(),
|
||||||
);
|
];
|
||||||
$result = [
|
}
|
||||||
'message' => $e->getMessage()
|
|
||||||
];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($debug) {
|
if ($debug & self::INCLUDE_TRACE > 0) {
|
||||||
$debugging = $e->getPrevious() ?: $e;
|
$debugging = $e->getPrevious() ?: $e;
|
||||||
$result['trace'] = static::toSafeTrace($debugging->getTrace());
|
$result['trace'] = static::toSafeTrace($debugging->getTrace());
|
||||||
}
|
}
|
||||||
|
@ -4,10 +4,17 @@ namespace GraphQL\Error;
|
|||||||
/**
|
/**
|
||||||
* Class UserError
|
* Class UserError
|
||||||
*
|
*
|
||||||
* Error that can be safely displayed to client...
|
* Error caused by actions of GraphQL clients. Can be safely displayed to client...
|
||||||
*
|
*
|
||||||
* @package GraphQL\Error
|
* @package GraphQL\Error
|
||||||
*/
|
*/
|
||||||
class UserError extends InvariantViolation
|
class UserError extends \RuntimeException implements ClientAware
|
||||||
{
|
{
|
||||||
|
/**
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
public function isClientSafe()
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -2,6 +2,7 @@
|
|||||||
namespace GraphQL\Executor;
|
namespace GraphQL\Executor;
|
||||||
|
|
||||||
use GraphQL\Error\Error;
|
use GraphQL\Error\Error;
|
||||||
|
use GraphQL\Error\FormattedError;
|
||||||
|
|
||||||
class ExecutionResult implements \JsonSerializable
|
class ExecutionResult implements \JsonSerializable
|
||||||
{
|
{
|
||||||
@ -23,7 +24,7 @@ class ExecutionResult implements \JsonSerializable
|
|||||||
/**
|
/**
|
||||||
* @var callable
|
* @var callable
|
||||||
*/
|
*/
|
||||||
private $errorFormatter = ['GraphQL\Error\Error', 'formatError'];
|
private $errorFormatter;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param array $data
|
* @param array $data
|
||||||
@ -48,14 +49,26 @@ class ExecutionResult implements \JsonSerializable
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* @param bool|int $debug
|
||||||
* @return array
|
* @return array
|
||||||
*/
|
*/
|
||||||
public function toArray()
|
public function toArray($debug = false)
|
||||||
{
|
{
|
||||||
$result = [];
|
$result = [];
|
||||||
|
|
||||||
if (!empty($this->errors)) {
|
if (!empty($this->errors)) {
|
||||||
$result['errors'] = array_map($this->errorFormatter, $this->errors);
|
if ($debug) {
|
||||||
|
$errorFormatter = function($e) use ($debug) {
|
||||||
|
return FormattedError::createFromException($e, $debug);
|
||||||
|
};
|
||||||
|
} else if (!$this->errorFormatter) {
|
||||||
|
$errorFormatter = function($e) {
|
||||||
|
return FormattedError::createFromException($e, false);
|
||||||
|
};
|
||||||
|
} else {
|
||||||
|
$errorFormatter = $this->errorFormatter;
|
||||||
|
}
|
||||||
|
$result['errors'] = array_map($errorFormatter, $this->errors);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (null !== $this->data) {
|
if (null !== $this->data) {
|
||||||
|
@ -2,6 +2,7 @@
|
|||||||
namespace GraphQL\Tests\Executor;
|
namespace GraphQL\Tests\Executor;
|
||||||
|
|
||||||
use GraphQL\Deferred;
|
use GraphQL\Deferred;
|
||||||
|
use GraphQL\Error\UserError;
|
||||||
use GraphQL\Error\Warning;
|
use GraphQL\Error\Warning;
|
||||||
use GraphQL\GraphQL;
|
use GraphQL\GraphQL;
|
||||||
use GraphQL\Schema;
|
use GraphQL\Schema;
|
||||||
@ -120,7 +121,7 @@ class AbstractPromiseTest extends \PHPUnit_Framework_TestCase
|
|||||||
'interfaces' => [$PetType],
|
'interfaces' => [$PetType],
|
||||||
'isTypeOf' => function () {
|
'isTypeOf' => function () {
|
||||||
return new Deferred(function () {
|
return new Deferred(function () {
|
||||||
throw new \Exception('We are testing this error');
|
throw new UserError('We are testing this error');
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
'fields' => [
|
'fields' => [
|
||||||
@ -578,7 +579,7 @@ class AbstractPromiseTest extends \PHPUnit_Framework_TestCase
|
|||||||
'name' => 'Pet',
|
'name' => 'Pet',
|
||||||
'resolveType' => function () {
|
'resolveType' => function () {
|
||||||
return new Deferred(function () {
|
return new Deferred(function () {
|
||||||
throw new \Exception('We are testing this error');
|
throw new UserError('We are testing this error');
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
'fields' => [
|
'fields' => [
|
||||||
|
@ -5,6 +5,7 @@ require_once __DIR__ . '/TestClasses.php';
|
|||||||
|
|
||||||
use GraphQL\Deferred;
|
use GraphQL\Deferred;
|
||||||
use GraphQL\Error\Error;
|
use GraphQL\Error\Error;
|
||||||
|
use GraphQL\Error\UserError;
|
||||||
use GraphQL\Executor\Executor;
|
use GraphQL\Executor\Executor;
|
||||||
use GraphQL\Language\Parser;
|
use GraphQL\Language\Parser;
|
||||||
use GraphQL\Schema;
|
use GraphQL\Schema;
|
||||||
@ -371,55 +372,55 @@ class ExecutorTest extends \PHPUnit_Framework_TestCase
|
|||||||
return 'sync';
|
return 'sync';
|
||||||
},
|
},
|
||||||
'syncError' => function () {
|
'syncError' => function () {
|
||||||
throw new \Exception('Error getting syncError');
|
throw new UserError('Error getting syncError');
|
||||||
},
|
},
|
||||||
'syncRawError' => function() {
|
'syncRawError' => function() {
|
||||||
throw new \Exception('Error getting syncRawError');
|
throw new UserError('Error getting syncRawError');
|
||||||
},
|
},
|
||||||
// inherited from JS reference implementation, but make no sense in this PHP impl
|
// inherited from JS reference implementation, but make no sense in this PHP impl
|
||||||
// leaving it just to simplify migrations from newer js versions
|
// leaving it just to simplify migrations from newer js versions
|
||||||
'syncReturnError' => function() {
|
'syncReturnError' => function() {
|
||||||
return new \Exception('Error getting syncReturnError');
|
return new UserError('Error getting syncReturnError');
|
||||||
},
|
},
|
||||||
'syncReturnErrorList' => function () {
|
'syncReturnErrorList' => function () {
|
||||||
return [
|
return [
|
||||||
'sync0',
|
'sync0',
|
||||||
new \Exception('Error getting syncReturnErrorList1'),
|
new UserError('Error getting syncReturnErrorList1'),
|
||||||
'sync2',
|
'sync2',
|
||||||
new \Exception('Error getting syncReturnErrorList3')
|
new UserError('Error getting syncReturnErrorList3')
|
||||||
];
|
];
|
||||||
},
|
},
|
||||||
'async' => function() {
|
'async' => function() {
|
||||||
return new Deferred(function() { return 'async'; });
|
return new Deferred(function() { return 'async'; });
|
||||||
},
|
},
|
||||||
'asyncReject' => function() {
|
'asyncReject' => function() {
|
||||||
return new Deferred(function() { throw new \Exception('Error getting asyncReject'); });
|
return new Deferred(function() { throw new UserError('Error getting asyncReject'); });
|
||||||
},
|
},
|
||||||
'asyncRawReject' => function () {
|
'asyncRawReject' => function () {
|
||||||
return new Deferred(function() {
|
return new Deferred(function() {
|
||||||
throw new \Exception('Error getting asyncRawReject');
|
throw new UserError('Error getting asyncRawReject');
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
'asyncEmptyReject' => function () {
|
'asyncEmptyReject' => function () {
|
||||||
return new Deferred(function() {
|
return new Deferred(function() {
|
||||||
throw new \Exception();
|
throw new UserError();
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
'asyncError' => function() {
|
'asyncError' => function() {
|
||||||
return new Deferred(function() {
|
return new Deferred(function() {
|
||||||
throw new \Exception('Error getting asyncError');
|
throw new UserError('Error getting asyncError');
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
// inherited from JS reference implementation, but make no sense in this PHP impl
|
// inherited from JS reference implementation, but make no sense in this PHP impl
|
||||||
// leaving it just to simplify migrations from newer js versions
|
// leaving it just to simplify migrations from newer js versions
|
||||||
'asyncRawError' => function() {
|
'asyncRawError' => function() {
|
||||||
return new Deferred(function() {
|
return new Deferred(function() {
|
||||||
throw new \Exception('Error getting asyncRawError');
|
throw new UserError('Error getting asyncRawError');
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
'asyncReturnError' => function() {
|
'asyncReturnError' => function() {
|
||||||
return new Deferred(function() {
|
return new Deferred(function() {
|
||||||
throw new \Exception('Error getting asyncReturnError');
|
throw new UserError('Error getting asyncReturnError');
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
@ -3,6 +3,7 @@
|
|||||||
namespace GraphQL\Tests\Executor;
|
namespace GraphQL\Tests\Executor;
|
||||||
|
|
||||||
use GraphQL\Deferred;
|
use GraphQL\Deferred;
|
||||||
|
use GraphQL\Error\UserError;
|
||||||
use GraphQL\Executor\Executor;
|
use GraphQL\Executor\Executor;
|
||||||
use GraphQL\Error\FormattedError;
|
use GraphQL\Error\FormattedError;
|
||||||
use GraphQL\Language\Parser;
|
use GraphQL\Language\Parser;
|
||||||
@ -72,7 +73,7 @@ class ListsTest extends \PHPUnit_Framework_TestCase
|
|||||||
$this->checkHandlesNullableLists(
|
$this->checkHandlesNullableLists(
|
||||||
function () {
|
function () {
|
||||||
return new Deferred(function () {
|
return new Deferred(function () {
|
||||||
throw new \Exception('bad');
|
throw new UserError('bad');
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
[
|
[
|
||||||
@ -131,7 +132,7 @@ class ListsTest extends \PHPUnit_Framework_TestCase
|
|||||||
return 1;
|
return 1;
|
||||||
}),
|
}),
|
||||||
new Deferred(function() {
|
new Deferred(function() {
|
||||||
throw new \Exception('bad');
|
throw new UserError('bad');
|
||||||
}),
|
}),
|
||||||
new Deferred(function() {
|
new Deferred(function() {
|
||||||
return 2;
|
return 2;
|
||||||
@ -174,12 +175,13 @@ class ListsTest extends \PHPUnit_Framework_TestCase
|
|||||||
[
|
[
|
||||||
'data' => [ 'nest' => null ],
|
'data' => [ 'nest' => null ],
|
||||||
'errors' => [
|
'errors' => [
|
||||||
FormattedError::create(
|
[
|
||||||
'Cannot return null for non-nullable field DataType.test.',
|
'debugMessage' => 'Cannot return null for non-nullable field DataType.test.',
|
||||||
[ new SourceLocation(1, 10) ]
|
'locations' => [['line' => 1, 'column' => 10]]
|
||||||
)
|
]
|
||||||
]
|
]
|
||||||
]
|
],
|
||||||
|
true
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -210,19 +212,20 @@ class ListsTest extends \PHPUnit_Framework_TestCase
|
|||||||
[
|
[
|
||||||
'data' => [ 'nest' => null ],
|
'data' => [ 'nest' => null ],
|
||||||
'errors' => [
|
'errors' => [
|
||||||
FormattedError::create(
|
[
|
||||||
'Cannot return null for non-nullable field DataType.test.',
|
'debugMessage' => 'Cannot return null for non-nullable field DataType.test.',
|
||||||
[ new SourceLocation(1, 10) ]
|
'locations' => [['line' => 1, 'column' => 10]]
|
||||||
)
|
]
|
||||||
]
|
]
|
||||||
]
|
],
|
||||||
|
true
|
||||||
);
|
);
|
||||||
|
|
||||||
// Rejected
|
// Rejected
|
||||||
$this->checkHandlesNonNullableLists(
|
$this->checkHandlesNonNullableLists(
|
||||||
function () {
|
function () {
|
||||||
return new Deferred(function() {
|
return new Deferred(function() {
|
||||||
throw new \Exception('bad');
|
throw new UserError('bad');
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
[
|
[
|
||||||
@ -280,7 +283,7 @@ class ListsTest extends \PHPUnit_Framework_TestCase
|
|||||||
return 1;
|
return 1;
|
||||||
}),
|
}),
|
||||||
new Deferred(function() {
|
new Deferred(function() {
|
||||||
throw new \Exception('bad');
|
throw new UserError('bad');
|
||||||
}),
|
}),
|
||||||
new Deferred(function() {
|
new Deferred(function() {
|
||||||
return 2;
|
return 2;
|
||||||
@ -317,12 +320,13 @@ class ListsTest extends \PHPUnit_Framework_TestCase
|
|||||||
[
|
[
|
||||||
'data' => [ 'nest' => [ 'test' => null ] ],
|
'data' => [ 'nest' => [ 'test' => null ] ],
|
||||||
'errors' => [
|
'errors' => [
|
||||||
FormattedError::create(
|
[
|
||||||
'Cannot return null for non-nullable field DataType.test.',
|
'debugMessage' => 'Cannot return null for non-nullable field DataType.test.',
|
||||||
[ new SourceLocation(1, 10) ]
|
'locations' => [ ['line' => 1, 'column' => 10] ]
|
||||||
)
|
]
|
||||||
]
|
]
|
||||||
]
|
],
|
||||||
|
true
|
||||||
);
|
);
|
||||||
|
|
||||||
// Returns null
|
// Returns null
|
||||||
@ -353,12 +357,13 @@ class ListsTest extends \PHPUnit_Framework_TestCase
|
|||||||
[
|
[
|
||||||
'data' => [ 'nest' => [ 'test' => null ] ],
|
'data' => [ 'nest' => [ 'test' => null ] ],
|
||||||
'errors' => [
|
'errors' => [
|
||||||
FormattedError::create(
|
[
|
||||||
'Cannot return null for non-nullable field DataType.test.',
|
'debugMessage' => 'Cannot return null for non-nullable field DataType.test.',
|
||||||
[ new SourceLocation(1, 10) ]
|
'locations' => [['line' => 1, 'column' => 10]]
|
||||||
)
|
]
|
||||||
]
|
]
|
||||||
]
|
],
|
||||||
|
true
|
||||||
);
|
);
|
||||||
|
|
||||||
// Returns null
|
// Returns null
|
||||||
@ -373,7 +378,7 @@ class ListsTest extends \PHPUnit_Framework_TestCase
|
|||||||
$this->checkHandlesListOfNonNulls(
|
$this->checkHandlesListOfNonNulls(
|
||||||
function () {
|
function () {
|
||||||
return new Deferred(function() {
|
return new Deferred(function() {
|
||||||
throw new \Exception('bad');
|
throw new UserError('bad');
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
[
|
[
|
||||||
@ -425,7 +430,7 @@ class ListsTest extends \PHPUnit_Framework_TestCase
|
|||||||
return 1;
|
return 1;
|
||||||
}),
|
}),
|
||||||
new Deferred(function() {
|
new Deferred(function() {
|
||||||
throw new \Exception('bad');
|
throw new UserError('bad');
|
||||||
}),
|
}),
|
||||||
new Deferred(function() {
|
new Deferred(function() {
|
||||||
return 2;
|
return 2;
|
||||||
@ -463,12 +468,13 @@ class ListsTest extends \PHPUnit_Framework_TestCase
|
|||||||
[
|
[
|
||||||
'data' => [ 'nest' => null ],
|
'data' => [ 'nest' => null ],
|
||||||
'errors' => [
|
'errors' => [
|
||||||
FormattedError::create(
|
[
|
||||||
'Cannot return null for non-nullable field DataType.test.',
|
'debugMessage' => 'Cannot return null for non-nullable field DataType.test.',
|
||||||
[ new SourceLocation(1, 10) ]
|
'locations' => [['line' => 1, 'column' => 10 ]]
|
||||||
)
|
]
|
||||||
]
|
]
|
||||||
]
|
],
|
||||||
|
true
|
||||||
);
|
);
|
||||||
|
|
||||||
// Returns null
|
// Returns null
|
||||||
@ -477,12 +483,13 @@ class ListsTest extends \PHPUnit_Framework_TestCase
|
|||||||
[
|
[
|
||||||
'data' => [ 'nest' => null ],
|
'data' => [ 'nest' => null ],
|
||||||
'errors' => [
|
'errors' => [
|
||||||
FormattedError::create(
|
[
|
||||||
'Cannot return null for non-nullable field DataType.test.',
|
'debugMessage' => 'Cannot return null for non-nullable field DataType.test.',
|
||||||
[ new SourceLocation(1, 10) ]
|
'locations' => [ ['line' => 1, 'column' => 10] ]
|
||||||
)
|
]
|
||||||
]
|
]
|
||||||
]
|
],
|
||||||
|
true
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -507,12 +514,13 @@ class ListsTest extends \PHPUnit_Framework_TestCase
|
|||||||
[
|
[
|
||||||
'data' => [ 'nest' => null ],
|
'data' => [ 'nest' => null ],
|
||||||
'errors' => [
|
'errors' => [
|
||||||
FormattedError::create(
|
[
|
||||||
'Cannot return null for non-nullable field DataType.test.',
|
'debugMessage' => 'Cannot return null for non-nullable field DataType.test.',
|
||||||
[ new SourceLocation(1, 10) ]
|
'locations' => [ ['line' => 1, 'column' => 10] ]
|
||||||
)
|
]
|
||||||
]
|
]
|
||||||
]
|
],
|
||||||
|
true
|
||||||
);
|
);
|
||||||
|
|
||||||
// Returns null
|
// Returns null
|
||||||
@ -523,19 +531,20 @@ class ListsTest extends \PHPUnit_Framework_TestCase
|
|||||||
[
|
[
|
||||||
'data' => [ 'nest' => null ],
|
'data' => [ 'nest' => null ],
|
||||||
'errors' => [
|
'errors' => [
|
||||||
FormattedError::create(
|
[
|
||||||
'Cannot return null for non-nullable field DataType.test.',
|
'debugMessage' => 'Cannot return null for non-nullable field DataType.test.',
|
||||||
[ new SourceLocation(1, 10) ]
|
'locations' => [ ['line' => 1, 'column' => 10] ]
|
||||||
)
|
]
|
||||||
]
|
]
|
||||||
]
|
],
|
||||||
|
true
|
||||||
);
|
);
|
||||||
|
|
||||||
// Rejected
|
// Rejected
|
||||||
$this->checkHandlesNonNullListOfNonNulls(
|
$this->checkHandlesNonNullListOfNonNulls(
|
||||||
function () {
|
function () {
|
||||||
return new Deferred(function() {
|
return new Deferred(function() {
|
||||||
throw new \Exception('bad');
|
throw new UserError('bad');
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
[
|
[
|
||||||
@ -586,12 +595,13 @@ class ListsTest extends \PHPUnit_Framework_TestCase
|
|||||||
[
|
[
|
||||||
'data' => [ 'nest' => null ],
|
'data' => [ 'nest' => null ],
|
||||||
'errors' => [
|
'errors' => [
|
||||||
FormattedError::create(
|
[
|
||||||
'Cannot return null for non-nullable field DataType.test.',
|
'debugMessage' => 'Cannot return null for non-nullable field DataType.test.',
|
||||||
[ new SourceLocation(1, 10) ]
|
'locations' => [['line' => 1, 'column' => 10]]
|
||||||
)
|
]
|
||||||
]
|
]
|
||||||
]
|
],
|
||||||
|
true
|
||||||
);
|
);
|
||||||
|
|
||||||
// Contains reject
|
// Contains reject
|
||||||
@ -602,7 +612,7 @@ class ListsTest extends \PHPUnit_Framework_TestCase
|
|||||||
return 1;
|
return 1;
|
||||||
}),
|
}),
|
||||||
new Deferred(function() {
|
new Deferred(function() {
|
||||||
throw new \Exception('bad');
|
throw new UserError('bad');
|
||||||
}),
|
}),
|
||||||
new Deferred(function() {
|
new Deferred(function() {
|
||||||
return 2;
|
return 2;
|
||||||
@ -628,25 +638,25 @@ class ListsTest extends \PHPUnit_Framework_TestCase
|
|||||||
$this->check($testType, $testData, $expected);
|
$this->check($testType, $testData, $expected);
|
||||||
}
|
}
|
||||||
|
|
||||||
private function checkHandlesNonNullableLists($testData, $expected)
|
private function checkHandlesNonNullableLists($testData, $expected, $debug = false)
|
||||||
{
|
{
|
||||||
$testType = Type::nonNull(Type::listOf(Type::int()));
|
$testType = Type::nonNull(Type::listOf(Type::int()));
|
||||||
$this->check($testType, $testData, $expected);
|
$this->check($testType, $testData, $expected, $debug);
|
||||||
}
|
}
|
||||||
|
|
||||||
private function checkHandlesListOfNonNulls($testData, $expected)
|
private function checkHandlesListOfNonNulls($testData, $expected, $debug = false)
|
||||||
{
|
{
|
||||||
$testType = Type::listOf(Type::nonNull(Type::int()));
|
$testType = Type::listOf(Type::nonNull(Type::int()));
|
||||||
$this->check($testType, $testData, $expected);
|
$this->check($testType, $testData, $expected, $debug);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function checkHandlesNonNullListOfNonNulls($testData, $expected)
|
public function checkHandlesNonNullListOfNonNulls($testData, $expected, $debug = false)
|
||||||
{
|
{
|
||||||
$testType = Type::nonNull(Type::listOf(Type::nonNull(Type::int())));
|
$testType = Type::nonNull(Type::listOf(Type::nonNull(Type::int())));
|
||||||
$this->check($testType, $testData, $expected);
|
$this->check($testType, $testData, $expected, $debug);
|
||||||
}
|
}
|
||||||
|
|
||||||
private function check($testType, $testData, $expected)
|
private function check($testType, $testData, $expected, $debug = false)
|
||||||
{
|
{
|
||||||
$data = ['test' => $testData];
|
$data = ['test' => $testData];
|
||||||
$dataType = null;
|
$dataType = null;
|
||||||
@ -675,6 +685,6 @@ class ListsTest extends \PHPUnit_Framework_TestCase
|
|||||||
$ast = Parser::parse('{ nest { test } }');
|
$ast = Parser::parse('{ nest { test } }');
|
||||||
|
|
||||||
$result = Executor::execute($schema, $ast, $data);
|
$result = Executor::execute($schema, $ast, $data);
|
||||||
$this->assertArraySubset($expected, $result->toArray());
|
$this->assertArraySubset($expected, $result->toArray($debug));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -106,17 +106,17 @@ class MutationsTest extends \PHPUnit_Framework_TestCase
|
|||||||
'sixth' => null,
|
'sixth' => null,
|
||||||
],
|
],
|
||||||
'errors' => [
|
'errors' => [
|
||||||
FormattedError::create(
|
[
|
||||||
'Cannot change the number',
|
'debugMessage' => 'Cannot change the number',
|
||||||
[new SourceLocation(8, 7)]
|
'locations' => [['line' => 8, 'column' => 7]]
|
||||||
),
|
],
|
||||||
FormattedError::create(
|
[
|
||||||
'Cannot change the number',
|
'debugMessage' => 'Cannot change the number',
|
||||||
[new SourceLocation(17, 7)]
|
'locations' => [['line' => 17, 'column' => 7]]
|
||||||
)
|
]
|
||||||
]
|
]
|
||||||
];
|
];
|
||||||
$this->assertArraySubset($expected, $mutationResult->toArray());
|
$this->assertArraySubset($expected, $mutationResult->toArray(true));
|
||||||
}
|
}
|
||||||
|
|
||||||
private function schema()
|
private function schema()
|
||||||
|
@ -3,6 +3,7 @@
|
|||||||
namespace GraphQL\Tests\Executor;
|
namespace GraphQL\Tests\Executor;
|
||||||
|
|
||||||
use GraphQL\Deferred;
|
use GraphQL\Deferred;
|
||||||
|
use GraphQL\Error\UserError;
|
||||||
use GraphQL\Executor\Executor;
|
use GraphQL\Executor\Executor;
|
||||||
use GraphQL\Error\FormattedError;
|
use GraphQL\Error\FormattedError;
|
||||||
use GraphQL\Language\Parser;
|
use GraphQL\Language\Parser;
|
||||||
@ -31,10 +32,10 @@ class NonNullTest extends \PHPUnit_Framework_TestCase
|
|||||||
|
|
||||||
public function setUp()
|
public function setUp()
|
||||||
{
|
{
|
||||||
$this->syncError = new \Exception('sync');
|
$this->syncError = new UserError('sync');
|
||||||
$this->nonNullSyncError = new \Exception('nonNullSync');
|
$this->nonNullSyncError = new UserError('nonNullSync');
|
||||||
$this->promiseError = new \Exception('promise');
|
$this->promiseError = new UserError('promise');
|
||||||
$this->nonNullPromiseError = new \Exception('nonNullPromise');
|
$this->nonNullPromiseError = new UserError('nonNullPromise');
|
||||||
|
|
||||||
$this->throwingData = [
|
$this->throwingData = [
|
||||||
'sync' => function () {
|
'sync' => function () {
|
||||||
@ -482,10 +483,13 @@ class NonNullTest extends \PHPUnit_Framework_TestCase
|
|||||||
'nest' => null
|
'nest' => null
|
||||||
],
|
],
|
||||||
'errors' => [
|
'errors' => [
|
||||||
FormattedError::create('Cannot return null for non-nullable field DataType.nonNullSync.', [new SourceLocation(4, 11)])
|
[
|
||||||
|
'debugMessage' => 'Cannot return null for non-nullable field DataType.nonNullSync.',
|
||||||
|
'locations' => [['line' => 4, 'column' => 11]]
|
||||||
|
]
|
||||||
]
|
]
|
||||||
];
|
];
|
||||||
$this->assertArraySubset($expected, Executor::execute($this->schema, $ast, $this->nullingData, null, [], 'Q')->toArray());
|
$this->assertArraySubset($expected, Executor::execute($this->schema, $ast, $this->nullingData, null, [], 'Q')->toArray(true));
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testNullsASynchronouslyReturnedObjectThatContainsANonNullableFieldThatReturnsNullInAPromise()
|
public function testNullsASynchronouslyReturnedObjectThatContainsANonNullableFieldThatReturnsNullInAPromise()
|
||||||
@ -505,11 +509,17 @@ class NonNullTest extends \PHPUnit_Framework_TestCase
|
|||||||
'nest' => null,
|
'nest' => null,
|
||||||
],
|
],
|
||||||
'errors' => [
|
'errors' => [
|
||||||
FormattedError::create('Cannot return null for non-nullable field DataType.nonNullPromise.', [new SourceLocation(4, 11)]),
|
[
|
||||||
|
'debugMessage' => 'Cannot return null for non-nullable field DataType.nonNullPromise.',
|
||||||
|
'locations' => [['line' => 4, 'column' => 11]]
|
||||||
|
],
|
||||||
]
|
]
|
||||||
];
|
];
|
||||||
|
|
||||||
$this->assertArraySubset($expected, Executor::execute($this->schema, $ast, $this->nullingData, null, [], 'Q')->toArray());
|
$this->assertArraySubset(
|
||||||
|
$expected,
|
||||||
|
Executor::execute($this->schema, $ast, $this->nullingData, null, [], 'Q')->toArray(true)
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testNullsAnObjectReturnedInAPromiseThatContainsANonNullableFieldThatReturnsNullSynchronously()
|
public function testNullsAnObjectReturnedInAPromiseThatContainsANonNullableFieldThatReturnsNullSynchronously()
|
||||||
@ -529,11 +539,17 @@ class NonNullTest extends \PHPUnit_Framework_TestCase
|
|||||||
'promiseNest' => null,
|
'promiseNest' => null,
|
||||||
],
|
],
|
||||||
'errors' => [
|
'errors' => [
|
||||||
FormattedError::create('Cannot return null for non-nullable field DataType.nonNullSync.', [new SourceLocation(4, 11)]),
|
[
|
||||||
|
'debugMessage' => 'Cannot return null for non-nullable field DataType.nonNullSync.',
|
||||||
|
'locations' => [['line' => 4, 'column' => 11]]
|
||||||
|
],
|
||||||
]
|
]
|
||||||
];
|
];
|
||||||
|
|
||||||
$this->assertArraySubset($expected, Executor::execute($this->schema, $ast, $this->nullingData, null, [], 'Q')->toArray());
|
$this->assertArraySubset(
|
||||||
|
$expected,
|
||||||
|
Executor::execute($this->schema, $ast, $this->nullingData, null, [], 'Q')->toArray(true)
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testNullsAnObjectReturnedInAPromiseThatContainsANonNullableFieldThatReturnsNullInaAPromise()
|
public function testNullsAnObjectReturnedInAPromiseThatContainsANonNullableFieldThatReturnsNullInaAPromise()
|
||||||
@ -553,11 +569,17 @@ class NonNullTest extends \PHPUnit_Framework_TestCase
|
|||||||
'promiseNest' => null,
|
'promiseNest' => null,
|
||||||
],
|
],
|
||||||
'errors' => [
|
'errors' => [
|
||||||
FormattedError::create('Cannot return null for non-nullable field DataType.nonNullPromise.', [new SourceLocation(4, 11)]),
|
[
|
||||||
|
'debugMessage' => 'Cannot return null for non-nullable field DataType.nonNullPromise.',
|
||||||
|
'locations' => [['line' => 4, 'column' => 11]]
|
||||||
|
],
|
||||||
]
|
]
|
||||||
];
|
];
|
||||||
|
|
||||||
$this->assertArraySubset($expected, Executor::execute($this->schema, $ast, $this->nullingData, null, [], 'Q')->toArray());
|
$this->assertArraySubset(
|
||||||
|
$expected,
|
||||||
|
Executor::execute($this->schema, $ast, $this->nullingData, null, [], 'Q')->toArray(true)
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testNullsAComplexTreeOfNullableFieldsThatReturnNull()
|
public function testNullsAComplexTreeOfNullableFieldsThatReturnNull()
|
||||||
@ -687,14 +709,17 @@ class NonNullTest extends \PHPUnit_Framework_TestCase
|
|||||||
'anotherPromiseNest' => null,
|
'anotherPromiseNest' => null,
|
||||||
],
|
],
|
||||||
'errors' => [
|
'errors' => [
|
||||||
FormattedError::create('Cannot return null for non-nullable field DataType.nonNullSync.', [new SourceLocation(8, 19)]),
|
['debugMessage' => 'Cannot return null for non-nullable field DataType.nonNullSync.', 'locations' => [ ['line' => 8, 'column' => 19]]],
|
||||||
FormattedError::create('Cannot return null for non-nullable field DataType.nonNullSync.', [new SourceLocation(19, 19)]),
|
['debugMessage' => 'Cannot return null for non-nullable field DataType.nonNullSync.', 'locations' => [ ['line' => 19, 'column' => 19]]],
|
||||||
FormattedError::create('Cannot return null for non-nullable field DataType.nonNullPromise.', [new SourceLocation(30, 19)]),
|
['debugMessage' => 'Cannot return null for non-nullable field DataType.nonNullPromise.', 'locations' => [ ['line' => 30, 'column' => 19]]],
|
||||||
FormattedError::create('Cannot return null for non-nullable field DataType.nonNullPromise.', [new SourceLocation(41, 19)]),
|
['debugMessage' => 'Cannot return null for non-nullable field DataType.nonNullPromise.', 'locations' => [ ['line' => 41, 'column' => 19]]],
|
||||||
]
|
]
|
||||||
];
|
];
|
||||||
|
|
||||||
$this->assertArraySubset($expected, Executor::execute($this->schema, $ast, $this->nullingData, null, [], 'Q')->toArray());
|
$this->assertArraySubset(
|
||||||
|
$expected,
|
||||||
|
Executor::execute($this->schema, $ast, $this->nullingData, null, [], 'Q')->toArray(true)
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -743,10 +768,16 @@ class NonNullTest extends \PHPUnit_Framework_TestCase
|
|||||||
|
|
||||||
$expected = [
|
$expected = [
|
||||||
'errors' => [
|
'errors' => [
|
||||||
FormattedError::create('Cannot return null for non-nullable field DataType.nonNullSync.', [new SourceLocation(2, 17)]),
|
[
|
||||||
|
'debugMessage' => 'Cannot return null for non-nullable field DataType.nonNullSync.',
|
||||||
|
'locations' => [['line' => 2, 'column' => 17]]
|
||||||
|
],
|
||||||
]
|
]
|
||||||
];
|
];
|
||||||
$this->assertArraySubset($expected, Executor::execute($this->schema, Parser::parse($doc), $this->nullingData)->toArray());
|
$this->assertArraySubset(
|
||||||
|
$expected,
|
||||||
|
Executor::execute($this->schema, Parser::parse($doc), $this->nullingData)->toArray(true)
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testNullsTheTopLevelIfAsyncNonNullableFieldResolvesNull()
|
public function testNullsTheTopLevelIfAsyncNonNullableFieldResolvesNull()
|
||||||
@ -760,10 +791,16 @@ class NonNullTest extends \PHPUnit_Framework_TestCase
|
|||||||
$expected = [
|
$expected = [
|
||||||
'data' => null,
|
'data' => null,
|
||||||
'errors' => [
|
'errors' => [
|
||||||
FormattedError::create('Cannot return null for non-nullable field DataType.nonNullPromise.', [new SourceLocation(2, 17)]),
|
[
|
||||||
|
'debugMessage' => 'Cannot return null for non-nullable field DataType.nonNullPromise.',
|
||||||
|
'locations' => [['line' => 2, 'column' => 17]]
|
||||||
|
],
|
||||||
]
|
]
|
||||||
];
|
];
|
||||||
|
|
||||||
$this->assertArraySubset($expected, Executor::execute($this->schema, $ast, $this->nullingData, null, [], 'Q')->toArray());
|
$this->assertArraySubset(
|
||||||
|
$expected,
|
||||||
|
Executor::execute($this->schema, $ast, $this->nullingData, null, [], 'Q')->toArray(true)
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -48,7 +48,7 @@ class QueryExecutionTest extends \PHPUnit_Framework_TestCase
|
|||||||
'fieldWithException' => [
|
'fieldWithException' => [
|
||||||
'type' => Type::string(),
|
'type' => Type::string(),
|
||||||
'resolve' => function($root, $args, $context, $info) {
|
'resolve' => function($root, $args, $context, $info) {
|
||||||
throw new \Exception("This is the exception we want");
|
throw new UserError("This is the exception we want");
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
'testContextAndRootValue' => [
|
'testContextAndRootValue' => [
|
||||||
@ -122,10 +122,10 @@ class QueryExecutionTest extends \PHPUnit_Framework_TestCase
|
|||||||
],
|
],
|
||||||
'extensions' => [
|
'extensions' => [
|
||||||
'phpErrors' => [
|
'phpErrors' => [
|
||||||
['message' => 'deprecated', 'severity' => 16384],
|
['debugMessage' => 'deprecated', 'severity' => 16384],
|
||||||
['message' => 'notice', 'severity' => 1024],
|
['debugMessage' => 'notice', 'severity' => 1024],
|
||||||
['message' => 'warning', 'severity' => 512],
|
['debugMessage' => 'warning', 'severity' => 512],
|
||||||
['message' => 'Undefined index: test', 'severity' => 8],
|
['debugMessage' => 'Undefined index: test', 'severity' => 8],
|
||||||
]
|
]
|
||||||
]
|
]
|
||||||
];
|
];
|
||||||
@ -518,7 +518,7 @@ class QueryExecutionTest extends \PHPUnit_Framework_TestCase
|
|||||||
private function assertQueryResultEquals($expected, $query, $variables = null)
|
private function assertQueryResultEquals($expected, $query, $variables = null)
|
||||||
{
|
{
|
||||||
$result = $this->executeQuery($query, $variables);
|
$result = $this->executeQuery($query, $variables);
|
||||||
$this->assertArraySubset($expected, $result->toArray());
|
$this->assertArraySubset($expected, $result->toArray(true));
|
||||||
return $result;
|
return $result;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -6,9 +6,6 @@ use GraphQL\Error\UserError;
|
|||||||
use GraphQL\Server\Helper;
|
use GraphQL\Server\Helper;
|
||||||
use GraphQL\Server\OperationParams;
|
use GraphQL\Server\OperationParams;
|
||||||
|
|
||||||
/**
|
|
||||||
* @backupGlobals enabled
|
|
||||||
*/
|
|
||||||
class RequestParsingTest extends \PHPUnit_Framework_TestCase
|
class RequestParsingTest extends \PHPUnit_Framework_TestCase
|
||||||
{
|
{
|
||||||
public function testParsesGraphqlRequest()
|
public function testParsesGraphqlRequest()
|
||||||
|
@ -3,6 +3,7 @@ namespace GraphQL\Tests;
|
|||||||
|
|
||||||
use GraphQL\Error\FormattedError;
|
use GraphQL\Error\FormattedError;
|
||||||
use GraphQL\Error\InvariantViolation;
|
use GraphQL\Error\InvariantViolation;
|
||||||
|
use GraphQL\Error\UserError;
|
||||||
use GraphQL\Executor\Promise\Adapter\SyncPromiseAdapter;
|
use GraphQL\Executor\Promise\Adapter\SyncPromiseAdapter;
|
||||||
use GraphQL\Schema;
|
use GraphQL\Schema;
|
||||||
use GraphQL\Server;
|
use GraphQL\Server;
|
||||||
@ -465,7 +466,7 @@ class ServerTest extends \PHPUnit_Framework_TestCase
|
|||||||
'withException' => [
|
'withException' => [
|
||||||
'type' => Type::string(),
|
'type' => Type::string(),
|
||||||
'resolve' => function() {
|
'resolve' => function() {
|
||||||
throw new \Exception("Error");
|
throw new UserError("Error");
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
]
|
]
|
||||||
|
@ -415,12 +415,12 @@ class EnumTypeTest extends \PHPUnit_Framework_TestCase
|
|||||||
*/
|
*/
|
||||||
public function testMayBeInternallyRepresentedWithComplexValues()
|
public function testMayBeInternallyRepresentedWithComplexValues()
|
||||||
{
|
{
|
||||||
$result = GraphQL::execute($this->schema, '{
|
$result = GraphQL::executeAndReturnResult($this->schema, '{
|
||||||
first: complexEnum
|
first: complexEnum
|
||||||
second: complexEnum(fromEnum: TWO)
|
second: complexEnum(fromEnum: TWO)
|
||||||
good: complexEnum(provideGoodValue: true)
|
good: complexEnum(provideGoodValue: true)
|
||||||
bad: complexEnum(provideBadValue: true)
|
bad: complexEnum(provideBadValue: true)
|
||||||
}');
|
}')->toArray(true);
|
||||||
|
|
||||||
$expected = [
|
$expected = [
|
||||||
'data' => [
|
'data' => [
|
||||||
@ -430,7 +430,7 @@ class EnumTypeTest extends \PHPUnit_Framework_TestCase
|
|||||||
'bad' => null
|
'bad' => null
|
||||||
],
|
],
|
||||||
'errors' => [[
|
'errors' => [[
|
||||||
'message' =>
|
'debugMessage' =>
|
||||||
'Expected a value of type "Complex" but received: instance of ArrayObject',
|
'Expected a value of type "Complex" but received: instance of ArrayObject',
|
||||||
'locations' => [['line' => 5, 'column' => 9]]
|
'locations' => [['line' => 5, 'column' => 9]]
|
||||||
]]
|
]]
|
||||||
@ -460,11 +460,11 @@ class EnumTypeTest extends \PHPUnit_Framework_TestCase
|
|||||||
[
|
[
|
||||||
'data' => ['first' => 'ONE', 'second' => 'TWO', 'third' => null],
|
'data' => ['first' => 'ONE', 'second' => 'TWO', 'third' => null],
|
||||||
'errors' => [[
|
'errors' => [[
|
||||||
'message' => 'Expected a value of type "SimpleEnum" but received: "WRONG"',
|
'debugMessage' => 'Expected a value of type "SimpleEnum" but received: "WRONG"',
|
||||||
'locations' => [['line' => 4, 'column' => 13]]
|
'locations' => [['line' => 4, 'column' => 13]]
|
||||||
]]
|
]]
|
||||||
],
|
],
|
||||||
GraphQL::execute($this->schema, $q)
|
GraphQL::executeAndReturnResult($this->schema, $q)->toArray(true)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -117,14 +117,14 @@ class ScalarSerializationTest extends \PHPUnit_Framework_TestCase
|
|||||||
try {
|
try {
|
||||||
$floatType->serialize('one');
|
$floatType->serialize('one');
|
||||||
$this->fail('Expected exception was not thrown');
|
$this->fail('Expected exception was not thrown');
|
||||||
} catch (UserError $e) {
|
} catch (InvariantViolation $e) {
|
||||||
$this->assertEquals('Float cannot represent non numeric value: "one"', $e->getMessage());
|
$this->assertEquals('Float cannot represent non numeric value: "one"', $e->getMessage());
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
$floatType->serialize('');
|
$floatType->serialize('');
|
||||||
$this->fail('Expected exception was not thrown');
|
$this->fail('Expected exception was not thrown');
|
||||||
} catch (UserError $e) {
|
} catch (InvariantViolation $e) {
|
||||||
$this->assertEquals('Float cannot represent non numeric value: (empty string)', $e->getMessage());
|
$this->assertEquals('Float cannot represent non numeric value: (empty string)', $e->getMessage());
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -149,14 +149,14 @@ class ScalarSerializationTest extends \PHPUnit_Framework_TestCase
|
|||||||
try {
|
try {
|
||||||
$stringType->serialize([]);
|
$stringType->serialize([]);
|
||||||
$this->fail('Expected exception was not thrown');
|
$this->fail('Expected exception was not thrown');
|
||||||
} catch (UserError $e) {
|
} catch (InvariantViolation $e) {
|
||||||
$this->assertEquals('String cannot represent non scalar value: array', $e->getMessage());
|
$this->assertEquals('String cannot represent non scalar value: array(0)', $e->getMessage());
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
$stringType->serialize(new \stdClass());
|
$stringType->serialize(new \stdClass());
|
||||||
$this->fail('Expected exception was not thrown');
|
$this->fail('Expected exception was not thrown');
|
||||||
} catch (UserError $e) {
|
} catch (InvariantViolation $e) {
|
||||||
$this->assertEquals('String cannot represent non scalar value: instance of stdClass', $e->getMessage());
|
$this->assertEquals('String cannot represent non scalar value: instance of stdClass', $e->getMessage());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user