mirror of
https://github.com/retailcrm/graphql-php.git
synced 2024-11-21 20:36:05 +03:00
Add Debug::RETHROW_UNSAFE_EXCEPTIONS flag
This allows for a more granular default for which Exceptions are thrown, taking the settings made through the ClientAware interface into account. - Add docs - Add a test case - Differentiate clearly from other test
This commit is contained in:
parent
503ac4619a
commit
326e0b4719
@ -84,7 +84,7 @@ To change default **"Internal server error"** message to something else, use:
|
|||||||
GraphQL\Error\FormattedError::setInternalErrorMessage("Unexpected error");
|
GraphQL\Error\FormattedError::setInternalErrorMessage("Unexpected error");
|
||||||
```
|
```
|
||||||
|
|
||||||
#Debugging tools
|
# Debugging tools
|
||||||
|
|
||||||
During development or debugging use `$result->toArray(true)` to add **debugMessage** key to
|
During development or debugging use `$result->toArray(true)` to add **debugMessage** key to
|
||||||
each formatted error entry. If you also want to add exception trace - pass flags instead:
|
each formatted error entry. If you also want to add exception trace - pass flags instead:
|
||||||
@ -116,7 +116,7 @@ This will make each error entry to look like this:
|
|||||||
];
|
];
|
||||||
```
|
```
|
||||||
|
|
||||||
If you prefer first resolver exception to be re-thrown, use following flags:
|
If you prefer the first resolver exception to be re-thrown, use following flags:
|
||||||
```php
|
```php
|
||||||
<?php
|
<?php
|
||||||
use GraphQL\GraphQL;
|
use GraphQL\GraphQL;
|
||||||
@ -127,6 +127,9 @@ $debug = Debug::INCLUDE_DEBUG_MESSAGE | Debug::RETHROW_INTERNAL_EXCEPTIONS;
|
|||||||
$result = GraphQL::executeQuery(/*args*/)->toArray($debug);
|
$result = GraphQL::executeQuery(/*args*/)->toArray($debug);
|
||||||
```
|
```
|
||||||
|
|
||||||
|
If you only want to re-throw Exceptions that are not marked as safe through the `ClientAware` interface, use
|
||||||
|
the flag `Debug::RETHROW_UNSAFE_EXCEPTIONS`.
|
||||||
|
|
||||||
# Custom Error Handling and Formatting
|
# Custom Error Handling and Formatting
|
||||||
It is possible to define custom **formatter** and **handler** for result errors.
|
It is possible to define custom **formatter** and **handler** for result errors.
|
||||||
|
|
||||||
|
@ -12,4 +12,5 @@ class Debug
|
|||||||
const INCLUDE_DEBUG_MESSAGE = 1;
|
const INCLUDE_DEBUG_MESSAGE = 1;
|
||||||
const INCLUDE_TRACE = 2;
|
const INCLUDE_TRACE = 2;
|
||||||
const RETHROW_INTERNAL_EXCEPTIONS = 4;
|
const RETHROW_INTERNAL_EXCEPTIONS = 4;
|
||||||
|
const RETHROW_UNSAFE_EXCEPTIONS = 8;
|
||||||
}
|
}
|
||||||
|
@ -235,9 +235,7 @@ class FormattedError
|
|||||||
|
|
||||||
$debug = (int) $debug;
|
$debug = (int) $debug;
|
||||||
|
|
||||||
$isInternal = ! $e instanceof ClientAware || ! $e->isClientSafe();
|
if ($debug & Debug::RETHROW_INTERNAL_EXCEPTIONS) {
|
||||||
|
|
||||||
if (($debug & Debug::RETHROW_INTERNAL_EXCEPTIONS) && $isInternal) {
|
|
||||||
if (! $e instanceof Error) {
|
if (! $e instanceof Error) {
|
||||||
throw $e;
|
throw $e;
|
||||||
}
|
}
|
||||||
@ -247,7 +245,15 @@ class FormattedError
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (($debug & Debug::INCLUDE_DEBUG_MESSAGE) && $isInternal) {
|
$isUnsafe = ! $e instanceof ClientAware || ! $e->isClientSafe();
|
||||||
|
|
||||||
|
if(($debug & Debug::RETHROW_UNSAFE_EXCEPTIONS) && $isUnsafe){
|
||||||
|
if ($e->getPrevious()) {
|
||||||
|
throw $e->getPrevious();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (($debug & Debug::INCLUDE_DEBUG_MESSAGE) && $isUnsafe) {
|
||||||
// Displaying debugMessage as a first entry:
|
// Displaying debugMessage as a first entry:
|
||||||
$formattedError = ['debugMessage' => $e->getMessage()] + $formattedError;
|
$formattedError = ['debugMessage' => $e->getMessage()] + $formattedError;
|
||||||
}
|
}
|
||||||
|
@ -1,11 +1,9 @@
|
|||||||
<?php
|
<?php
|
||||||
namespace GraphQL\Tests\Server;
|
namespace GraphQL\Tests\Server;
|
||||||
|
|
||||||
use GraphQL\Deferred;
|
|
||||||
use GraphQL\Error\Debug;
|
use GraphQL\Error\Debug;
|
||||||
use GraphQL\Error\Error;
|
use GraphQL\Error\Error;
|
||||||
use GraphQL\Error\InvariantViolation;
|
use GraphQL\Error\InvariantViolation;
|
||||||
use GraphQL\Error\UserError;
|
|
||||||
use GraphQL\Executor\ExecutionResult;
|
use GraphQL\Executor\ExecutionResult;
|
||||||
use GraphQL\Language\AST\DocumentNode;
|
use GraphQL\Language\AST\DocumentNode;
|
||||||
use GraphQL\Language\Parser;
|
use GraphQL\Language\Parser;
|
||||||
@ -64,20 +62,20 @@ class QueryExecutionTest extends ServerTestCase
|
|||||||
|
|
||||||
$query = '
|
$query = '
|
||||||
{
|
{
|
||||||
fieldWithException
|
fieldWithSafeException
|
||||||
f1
|
f1
|
||||||
}
|
}
|
||||||
';
|
';
|
||||||
|
|
||||||
$expected = [
|
$expected = [
|
||||||
'data' => [
|
'data' => [
|
||||||
'fieldWithException' => null,
|
'fieldWithSafeException' => null,
|
||||||
'f1' => 'f1'
|
'f1' => 'f1'
|
||||||
],
|
],
|
||||||
'errors' => [
|
'errors' => [
|
||||||
[
|
[
|
||||||
'message' => 'This is the exception we want',
|
'message' => 'This is the exception we want',
|
||||||
'path' => ['fieldWithException'],
|
'path' => ['fieldWithSafeException'],
|
||||||
'trace' => []
|
'trace' => []
|
||||||
]
|
]
|
||||||
]
|
]
|
||||||
@ -86,6 +84,18 @@ class QueryExecutionTest extends ServerTestCase
|
|||||||
$result = $this->executeQuery($query)->toArray();
|
$result = $this->executeQuery($query)->toArray();
|
||||||
$this->assertArraySubset($expected, $result);
|
$this->assertArraySubset($expected, $result);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function testRethrowUnsafeExceptions() : void
|
||||||
|
{
|
||||||
|
$this->config->setDebug(Debug::RETHROW_UNSAFE_EXCEPTIONS);
|
||||||
|
$this->expectException(UnsafeException::class);
|
||||||
|
|
||||||
|
$this->executeQuery('
|
||||||
|
{
|
||||||
|
fieldWithUnsafeException
|
||||||
|
}
|
||||||
|
')->toArray();
|
||||||
|
}
|
||||||
|
|
||||||
public function testPassesRootValueAndContext() : void
|
public function testPassesRootValueAndContext() : void
|
||||||
{
|
{
|
||||||
@ -240,7 +250,7 @@ class QueryExecutionTest extends ServerTestCase
|
|||||||
'query' => '{invalid}'
|
'query' => '{invalid}'
|
||||||
],
|
],
|
||||||
[
|
[
|
||||||
'query' => '{f1,fieldWithException}'
|
'query' => '{f1,fieldWithSafeException}'
|
||||||
]
|
]
|
||||||
];
|
];
|
||||||
|
|
||||||
@ -405,7 +415,7 @@ class QueryExecutionTest extends ServerTestCase
|
|||||||
'query' => '{invalid}'
|
'query' => '{invalid}'
|
||||||
],
|
],
|
||||||
[
|
[
|
||||||
'query' => '{f1,fieldWithException}'
|
'query' => '{f1,fieldWithSafeException}'
|
||||||
],
|
],
|
||||||
[
|
[
|
||||||
'query' => '
|
'query' => '
|
||||||
@ -427,7 +437,7 @@ class QueryExecutionTest extends ServerTestCase
|
|||||||
[
|
[
|
||||||
'data' => [
|
'data' => [
|
||||||
'f1' => 'f1',
|
'f1' => 'f1',
|
||||||
'fieldWithException' => null
|
'fieldWithSafeException' => null
|
||||||
],
|
],
|
||||||
'errors' => [
|
'errors' => [
|
||||||
['message' => 'This is the exception we want']
|
['message' => 'This is the exception we want']
|
||||||
@ -581,7 +591,7 @@ class QueryExecutionTest extends ServerTestCase
|
|||||||
return ['test' => 'formatted'];
|
return ['test' => 'formatted'];
|
||||||
});
|
});
|
||||||
|
|
||||||
$result = $this->executeQuery('{fieldWithException}');
|
$result = $this->executeQuery('{fieldWithSafeException}');
|
||||||
$this->assertFalse($called);
|
$this->assertFalse($called);
|
||||||
$formatted = $result->toArray();
|
$formatted = $result->toArray();
|
||||||
$expected = [
|
$expected = [
|
||||||
@ -620,7 +630,7 @@ class QueryExecutionTest extends ServerTestCase
|
|||||||
];
|
];
|
||||||
});
|
});
|
||||||
|
|
||||||
$result = $this->executeQuery('{fieldWithException,test: fieldWithException}');
|
$result = $this->executeQuery('{fieldWithSafeException,test: fieldWithSafeException}');
|
||||||
|
|
||||||
$this->assertFalse($called);
|
$this->assertFalse($called);
|
||||||
$formatted = $result->toArray();
|
$formatted = $result->toArray();
|
||||||
|
@ -3,6 +3,7 @@ namespace GraphQL\Tests\Server;
|
|||||||
|
|
||||||
|
|
||||||
use GraphQL\Deferred;
|
use GraphQL\Deferred;
|
||||||
|
use GraphQL\Error\ClientAware;
|
||||||
use GraphQL\Error\UserError;
|
use GraphQL\Error\UserError;
|
||||||
use GraphQL\Type\Definition\ObjectType;
|
use GraphQL\Type\Definition\ObjectType;
|
||||||
use GraphQL\Type\Definition\Type;
|
use GraphQL\Type\Definition\Type;
|
||||||
@ -34,10 +35,16 @@ abstract class ServerTestCase extends TestCase
|
|||||||
return $info->fieldName;
|
return $info->fieldName;
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
'fieldWithException' => [
|
'fieldWithSafeException' => [
|
||||||
'type' => Type::string(),
|
'type' => Type::string(),
|
||||||
'resolve' => function($root, $args, $context, $info) {
|
'resolve' => function() {
|
||||||
throw new UserError("This is the exception we want");
|
throw new UserError('This is the exception we want');
|
||||||
|
}
|
||||||
|
],
|
||||||
|
'fieldWithUnsafeException' => [
|
||||||
|
'type' => Type::string(),
|
||||||
|
'resolve' => function() {
|
||||||
|
throw new UnsafeException('This exception should not be shown to the user');
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
'testContextAndRootValue' => [
|
'testContextAndRootValue' => [
|
||||||
@ -92,3 +99,30 @@ abstract class ServerTestCase extends TestCase
|
|||||||
return $schema;
|
return $schema;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class UnsafeException extends \Exception implements ClientAware
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Returns true when exception message is safe to be displayed to a client.
|
||||||
|
*
|
||||||
|
* @api
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
public function isClientSafe()
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns string describing a category of the error.
|
||||||
|
*
|
||||||
|
* Value "graphql" is reserved for errors produced by query parsing or validation, do not use it.
|
||||||
|
*
|
||||||
|
* @api
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public function getCategory()
|
||||||
|
{
|
||||||
|
return 'unsafe';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user