Default Error Formatting: Display error category under extensions key (#389)

This commit is contained in:
Vladimir Razuvaev 2018-11-27 16:37:33 +07:00
parent 54263d50c0
commit c45fa1a4b1
6 changed files with 74 additions and 62 deletions

View File

@ -10,7 +10,7 @@ It is a one-line switch: `GraphQL::useExperimentalExecutor()`.<br>
(as it may become the default one in future) (as it may become the default one in future)
<br> <br>
<br> <br>
- Spec compliance: error category, debug information and extensions are displayed under `extensions` key - Spec compliance: error `category` and extensions are displayed under `extensions` key when using default formatting (#389)
- `AbstractValidationRule` renamed to `ValidationRule` (NS `GraphQL\Validator\Rules`) - `AbstractValidationRule` renamed to `ValidationRule` (NS `GraphQL\Validator\Rules`)
- `AbstractQuerySecurity` renamed to `QuerySecurityRule` (NS `GraphQL\Validator\Rules`) - `AbstractQuerySecurity` renamed to `QuerySecurityRule` (NS `GraphQL\Validator\Rules`)
- `FindBreakingChanges` renamed to `BreakingChangesFinder` (NS `GraphQL\Utils`) - `FindBreakingChanges` renamed to `BreakingChangesFinder` (NS `GraphQL\Utils`)

View File

@ -1,39 +1,10 @@
## Upgrade v0.12.x > dev-master ## Upgrade v0.12.x > dev-master
### Try it: Experimental Executor with improved performance ### Breaking (major): minimum supported version of PHP
It is disabled by default. To enable it, do the following
```php
<?php
use GraphQL\Executor\Executor;
use GraphQL\Experimental\Executor\CoroutineExecutor;
Executor::setImplementationFactory([CoroutineExecutor::class, 'create']);
```
**Please post your feedback about new executor at https://github.com/webonyx/graphql-php/issues/397
Especially if you had issues (because it may become the default in one of the next releases)**
### Breaking: minimum supported version of PHP
New minimum required version of PHP is **7.1+** New minimum required version of PHP is **7.1+**
### Breaking: multiple interfaces separated with & in SDL ### Breaking (major): default errors formatting changed according to spec
Before the change: **Category** and extensions assigned to errors are shown under `extensions` key
```graphql
type Foo implements Bar, Baz { field: Type }
```
After the change:
```graphql
type Foo implements Bar & Baz { field: Type }
```
To allow for an adaptive migration, use `allowLegacySDLImplementsInterfaces` option of parser:
```php
Parser::parse($source, [ 'allowLegacySDLImplementsInterfaces' => true])
```
### Breaking: errors formatting changed according to spec
Extensions assigned to errors are shown under `extensions` key
```php ```php
$e = new Error( $e = new Error(
'msg', 'msg',
@ -50,6 +21,7 @@ Formatting before the change:
'errors' => [ 'errors' => [
[ [
'message' => 'msg', 'message' => 'msg',
'category' => 'graphql',
'foo' => 'bar' 'foo' => 'bar'
] ]
] ]
@ -59,11 +31,47 @@ After the change:
'errors' => [ 'errors' => [
[ [
'message' => 'msg', 'message' => 'msg',
'extensions' => ['foo' => 'bar'], 'extensions' => [
'category' => 'graphql',
'foo' => 'bar',
],
] ]
] ]
``` ```
Note: if error extensions contain `category` key - it has a priority over default category.
You can always switch to [custom error formatting](https://webonyx.github.io/graphql-php/error-handling/#custom-error-handling-and-formatting) to revert to the old format.
### Try it: Experimental Executor with improved performance
It is disabled by default. To enable it, do the following
```php
<?php
use GraphQL\Executor\Executor;
use GraphQL\Experimental\Executor\CoroutineExecutor;
Executor::setImplementationFactory([CoroutineExecutor::class, 'create']);
```
**Please post your feedback about new executor at https://github.com/webonyx/graphql-php/issues/397
Especially if you had issues (because it may become the default in one of the next releases)**
### Breaking: multiple interfaces separated with & in SDL
Before the change:
```graphql
type Foo implements Bar, Baz { field: Type }
```
After the change:
```graphql
type Foo implements Bar & Baz { field: Type }
```
To allow for an adaptive migration, use `allowLegacySDLImplementsInterfaces` option of parser:
```php
Parser::parse($source, [ 'allowLegacySDLImplementsInterfaces' => true])
```
## Upgrade v0.11.x > v0.12.x ## Upgrade v0.11.x > v0.12.x

View File

@ -187,12 +187,16 @@ class FormattedError
if ($e instanceof ClientAware) { if ($e instanceof ClientAware) {
$formattedError = [ $formattedError = [
'message' => $e->isClientSafe() ? $e->getMessage() : $internalErrorMessage, 'message' => $e->isClientSafe() ? $e->getMessage() : $internalErrorMessage,
'extensions' => [
'category' => $e->getCategory(), 'category' => $e->getCategory(),
],
]; ];
} else { } else {
$formattedError = [ $formattedError = [
'message' => $internalErrorMessage, 'message' => $internalErrorMessage,
'extensions' => [
'category' => Error::CATEGORY_INTERNAL, 'category' => Error::CATEGORY_INTERNAL,
],
]; ];
} }
@ -210,7 +214,7 @@ class FormattedError
$formattedError['path'] = $e->path; $formattedError['path'] = $e->path;
} }
if (! empty($e->getExtensions())) { if (! empty($e->getExtensions())) {
$formattedError['extensions'] = $e->getExtensions(); $formattedError['extensions'] = $e->getExtensions() + $formattedError['extensions'];
} }
} }

View File

@ -420,7 +420,7 @@ class AbstractTest extends TestCase
'function or each possible type should provide an "isTypeOf" function.', 'function or each possible type should provide an "isTypeOf" function.',
'locations' => [['line' => 1, 'column' => 3]], 'locations' => [['line' => 1, 'column' => 3]],
'path' => ['foo'], 'path' => ['foo'],
'category' => 'internal', 'extensions' => ['category' => 'internal'],
], ],
], ],
]; ];

View File

@ -222,7 +222,7 @@ class VariablesTest extends TestCase
'{"a":"foo","b":"bar","c":null}; ' . '{"a":"foo","b":"bar","c":null}; ' .
'Expected non-nullable type String! not to be null at value.c.', 'Expected non-nullable type String! not to be null at value.c.',
'locations' => [['line' => 2, 'column' => 21]], 'locations' => [['line' => 2, 'column' => 21]],
'category' => 'graphql', 'extensions' => ['category' => 'graphql'],
], ],
], ],
]; ];
@ -239,7 +239,7 @@ class VariablesTest extends TestCase
'Variable "$input" got invalid value "foo bar"; ' . 'Variable "$input" got invalid value "foo bar"; ' .
'Expected type TestInputObject to be an object.', 'Expected type TestInputObject to be an object.',
'locations' => [['line' => 2, 'column' => 21]], 'locations' => [['line' => 2, 'column' => 21]],
'category' => 'graphql', 'extensions' => ['category' => 'graphql'],
], ],
], ],
]; ];
@ -256,7 +256,7 @@ class VariablesTest extends TestCase
'Variable "$input" got invalid value {"a":"foo","b":"bar"}; ' . 'Variable "$input" got invalid value {"a":"foo","b":"bar"}; ' .
'Field value.c of required type String! was not provided.', 'Field value.c of required type String! was not provided.',
'locations' => [['line' => 2, 'column' => 21]], 'locations' => [['line' => 2, 'column' => 21]],
'category' => 'graphql', 'extensions' => ['category' => 'graphql'],
], ],
], ],
]; ];
@ -278,14 +278,14 @@ class VariablesTest extends TestCase
'Variable "$input" got invalid value {"na":{"a":"foo"}}; ' . 'Variable "$input" got invalid value {"na":{"a":"foo"}}; ' .
'Field value.na.c of required type String! was not provided.', 'Field value.na.c of required type String! was not provided.',
'locations' => [['line' => 2, 'column' => 19]], 'locations' => [['line' => 2, 'column' => 19]],
'category' => 'graphql', 'extensions' => ['category' => 'graphql'],
], ],
[ [
'message' => 'message' =>
'Variable "$input" got invalid value {"na":{"a":"foo"}}; ' . 'Variable "$input" got invalid value {"na":{"a":"foo"}}; ' .
'Field value.nb of required type String! was not provided.', 'Field value.nb of required type String! was not provided.',
'locations' => [['line' => 2, 'column' => 19]], 'locations' => [['line' => 2, 'column' => 19]],
'category' => 'graphql', 'extensions' => ['category' => 'graphql'],
], ],
], ],
]; ];
@ -302,7 +302,7 @@ class VariablesTest extends TestCase
'{"a":"foo","b":"bar","c":"baz","extra":"dog"}; ' . '{"a":"foo","b":"bar","c":"baz","extra":"dog"}; ' .
'Field "extra" is not defined by type TestInputObject.', 'Field "extra" is not defined by type TestInputObject.',
'locations' => [['line' => 2, 'column' => 21]], 'locations' => [['line' => 2, 'column' => 21]],
'category' => 'graphql', 'extensions' => ['category' => 'graphql'],
], ],
], ],
]; ];
@ -434,7 +434,7 @@ class VariablesTest extends TestCase
[ [
'message' => 'Variable "$value" of required type "String!" was not provided.', 'message' => 'Variable "$value" of required type "String!" was not provided.',
'locations' => [['line' => 2, 'column' => 31]], 'locations' => [['line' => 2, 'column' => 31]],
'category' => 'graphql', 'extensions' => ['category' => 'graphql'],
], ],
], ],
]; ];
@ -459,7 +459,7 @@ class VariablesTest extends TestCase
'Variable "$value" got invalid value null; ' . 'Variable "$value" got invalid value null; ' .
'Expected non-nullable type String! not to be null.', 'Expected non-nullable type String! not to be null.',
'locations' => [['line' => 2, 'column' => 31]], 'locations' => [['line' => 2, 'column' => 31]],
'category' => 'graphql', 'extensions' => ['category' => 'graphql'],
], ],
], ],
]; ];
@ -511,7 +511,7 @@ class VariablesTest extends TestCase
'message' => 'Argument "input" of required type "String!" was not provided.', 'message' => 'Argument "input" of required type "String!" was not provided.',
'locations' => [['line' => 3, 'column' => 9]], 'locations' => [['line' => 3, 'column' => 9]],
'path' => ['fieldWithNonNullableStringInput'], 'path' => ['fieldWithNonNullableStringInput'],
'category' => 'graphql', 'extensions' => ['category' => 'graphql'],
], ],
], ],
]; ];
@ -538,10 +538,10 @@ class VariablesTest extends TestCase
'message' => 'message' =>
'Variable "$value" got invalid value [1,2,3]; Expected type ' . 'Variable "$value" got invalid value [1,2,3]; Expected type ' .
'String; String cannot represent an array value: [1,2,3]', 'String; String cannot represent an array value: [1,2,3]',
'category' => 'graphql',
'locations' => [ 'locations' => [
['line' => 2, 'column' => 31], ['line' => 2, 'column' => 31],
], ],
'extensions' => ['category' => 'graphql'],
], ],
], ],
]; ];
@ -581,7 +581,7 @@ class VariablesTest extends TestCase
'variable "$foo" which was not provided a runtime value.', 'variable "$foo" which was not provided a runtime value.',
'locations' => [['line' => 3, 'column' => 48]], 'locations' => [['line' => 3, 'column' => 48]],
'path' => ['fieldWithNonNullableStringInput'], 'path' => ['fieldWithNonNullableStringInput'],
'category' => 'graphql', 'extensions' => ['category' => 'graphql'],
], ],
], ],
]; ];
@ -652,7 +652,7 @@ class VariablesTest extends TestCase
'Variable "$input" got invalid value null; ' . 'Variable "$input" got invalid value null; ' .
'Expected non-nullable type [String]! not to be null.', 'Expected non-nullable type [String]! not to be null.',
'locations' => [['line' => 2, 'column' => 17]], 'locations' => [['line' => 2, 'column' => 17]],
'category' => 'graphql', 'extensions' => ['category' => 'graphql'],
], ],
], ],
]; ];
@ -737,7 +737,7 @@ class VariablesTest extends TestCase
'Variable "$input" got invalid value ["A",null,"B"]; ' . 'Variable "$input" got invalid value ["A",null,"B"]; ' .
'Expected non-nullable type String! not to be null at value[1].', 'Expected non-nullable type String! not to be null at value[1].',
'locations' => [['line' => 2, 'column' => 17]], 'locations' => [['line' => 2, 'column' => 17]],
'category' => 'graphql', 'extensions' => ['category' => 'graphql'],
], ],
], ],
]; ];
@ -762,7 +762,7 @@ class VariablesTest extends TestCase
'Variable "$input" got invalid value null; ' . 'Variable "$input" got invalid value null; ' .
'Expected non-nullable type [String!]! not to be null.', 'Expected non-nullable type [String!]! not to be null.',
'locations' => [['line' => 2, 'column' => 17]], 'locations' => [['line' => 2, 'column' => 17]],
'category' => 'graphql', 'extensions' => ['category' => 'graphql'],
], ],
], ],
]; ];
@ -804,7 +804,7 @@ class VariablesTest extends TestCase
'Variable "$input" got invalid value ["A",null,"B"]; ' . 'Variable "$input" got invalid value ["A",null,"B"]; ' .
'Expected non-nullable type String! not to be null at value[1].', 'Expected non-nullable type String! not to be null at value[1].',
'locations' => [['line' => 2, 'column' => 17]], 'locations' => [['line' => 2, 'column' => 17]],
'category' => 'graphql', 'extensions' => ['category' => 'graphql'],
], ],
], ],
]; ];
@ -830,7 +830,7 @@ class VariablesTest extends TestCase
'Variable "$input" expected value of type "TestType!" which cannot ' . 'Variable "$input" expected value of type "TestType!" which cannot ' .
'be used as an input type.', 'be used as an input type.',
'locations' => [['line' => 2, 'column' => 25]], 'locations' => [['line' => 2, 'column' => 25]],
'category' => 'graphql', 'extensions' => ['category' => 'graphql'],
], ],
], ],
]; ];
@ -857,7 +857,7 @@ class VariablesTest extends TestCase
'Variable "$input" expected value of type "UnknownType!" which ' . 'Variable "$input" expected value of type "UnknownType!" which ' .
'cannot be used as an input type.', 'cannot be used as an input type.',
'locations' => [['line' => 2, 'column' => 25]], 'locations' => [['line' => 2, 'column' => 25]],
'category' => 'graphql', 'extensions' => ['category' => 'graphql'],
], ],
], ],
]; ];
@ -910,7 +910,7 @@ class VariablesTest extends TestCase
'Argument "input" has invalid value WRONG_TYPE.', 'Argument "input" has invalid value WRONG_TYPE.',
'locations' => [['line' => 2, 'column' => 50]], 'locations' => [['line' => 2, 'column' => 50]],
'path' => ['fieldWithDefaultArgumentValue'], 'path' => ['fieldWithDefaultArgumentValue'],
'category' => 'graphql', 'extensions' => ['category' => 'graphql'],
], ],
], ],
]; ];

View File

@ -260,7 +260,7 @@ class QueryExecutionTest extends ServerTestCase
'errors' => [ 'errors' => [
[ [
'message' => 'Persisted queries are not supported by this server', 'message' => 'Persisted queries are not supported by this server',
'category' => 'request', 'extensions' => ['category' => 'request'],
], ],
], ],
]; ];
@ -291,7 +291,7 @@ class QueryExecutionTest extends ServerTestCase
'errors' => [ 'errors' => [
[ [
'message' => 'Batched queries are not supported by this server', 'message' => 'Batched queries are not supported by this server',
'category' => 'request', 'extensions' => ['category' => 'request'],
], ],
], ],
], ],
@ -299,7 +299,7 @@ class QueryExecutionTest extends ServerTestCase
'errors' => [ 'errors' => [
[ [
'message' => 'Batched queries are not supported by this server', 'message' => 'Batched queries are not supported by this server',
'category' => 'request', 'extensions' => ['category' => 'request'],
], ],
], ],
], ],
@ -342,7 +342,7 @@ class QueryExecutionTest extends ServerTestCase
'errors' => [ 'errors' => [
[ [
'message' => 'GET supports only query operation', 'message' => 'GET supports only query operation',
'category' => 'request', 'extensions' => ['category' => 'request'],
], ],
], ],
]; ];
@ -406,7 +406,7 @@ class QueryExecutionTest extends ServerTestCase
[ [
'message' => 'Cannot query field "invalid" on type "Query".', 'message' => 'Cannot query field "invalid" on type "Query".',
'locations' => [['line' => 1, 'column' => 2]], 'locations' => [['line' => 1, 'column' => 2]],
'category' => 'graphql', 'extensions' => ['category' => 'graphql'],
], ],
], ],
]; ];
@ -443,7 +443,7 @@ class QueryExecutionTest extends ServerTestCase
[ [
'message' => 'Cannot query field "invalid2" on type "Query".', 'message' => 'Cannot query field "invalid2" on type "Query".',
'locations' => [['line' => 1, 'column' => 2]], 'locations' => [['line' => 1, 'column' => 2]],
'category' => 'graphql', 'extensions' => ['category' => 'graphql'],
], ],
], ],
]; ];