Skip to content

Commit 416f9ba

Browse files
author
Jeremiah VALERIE
committed
Use event to handle error formatting
1 parent ed25b73 commit 416f9ba

26 files changed

+488
-352
lines changed

DependencyInjection/Configuration.php

Lines changed: 232 additions & 128 deletions
Large diffs are not rendered by default.

DependencyInjection/OverblogGraphQLExtension.php

Lines changed: 41 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -5,12 +5,16 @@
55
use GraphQL\Type\Schema;
66
use Overblog\GraphQLBundle\CacheWarmer\CompileCacheWarmer;
77
use Overblog\GraphQLBundle\Config\Processor\BuilderProcessor;
8+
use Overblog\GraphQLBundle\Error\ErrorHandler;
89
use Overblog\GraphQLBundle\Event\Events;
910
use Overblog\GraphQLBundle\EventListener\ClassLoaderListener;
1011
use Overblog\GraphQLBundle\EventListener\DebugListener;
12+
use Overblog\GraphQLBundle\EventListener\ErrorHandlerListener;
13+
use Overblog\GraphQLBundle\EventListener\ErrorLoggerListener;
1114
use Symfony\Component\Cache\Adapter\ArrayAdapter;
1215
use Symfony\Component\Config\FileLocator;
1316
use Symfony\Component\DependencyInjection\ContainerBuilder;
17+
use Symfony\Component\DependencyInjection\ContainerInterface;
1418
use Symfony\Component\DependencyInjection\Definition;
1519
use Symfony\Component\DependencyInjection\Extension\PrependExtensionInterface;
1620
use Symfony\Component\DependencyInjection\Loader\YamlFileLoader;
@@ -34,7 +38,7 @@ public function load(array $configs, ContainerBuilder $container)
3438
$this->setServicesAliases($config, $container);
3539
$this->setSchemaBuilderArguments($config, $container);
3640
$this->setSchemaArguments($config, $container);
37-
$this->setErrorHandlerArguments($config, $container);
41+
$this->setErrorHandler($config, $container);
3842
$this->setSecurity($config, $container);
3943
$this->setConfigBuilders($config, $container);
4044
$this->setDebugListener($config, $container);
@@ -58,7 +62,7 @@ public function prepend(ContainerBuilder $container)
5862

5963
public function getAlias()
6064
{
61-
return 'overblog_graphql';
65+
return Configuration::NAME;
6266
}
6367

6468
public function getConfiguration(array $config, ContainerBuilder $container)
@@ -167,28 +171,44 @@ private function setSecurity(array $config, ContainerBuilder $container)
167171
}
168172
}
169173

170-
private function setErrorHandlerArguments(array $config, ContainerBuilder $container)
174+
private function setErrorHandler(array $config, ContainerBuilder $container)
171175
{
172-
$errorHandlerDefinition = $container->getDefinition($this->getAlias().'.error_handler');
173-
174-
if (isset($config['definitions']['internal_error_message'])) {
175-
$errorHandlerDefinition->replaceArgument(0, $config['definitions']['internal_error_message']);
176-
}
177-
178-
if (isset($config['definitions']['map_exceptions_to_parent'])) {
179-
$errorHandlerDefinition->replaceArgument(
180-
3,
181-
$config['definitions']['map_exceptions_to_parent']
182-
);
183-
}
176+
if ($config['errors_handler']['enabled']) {
177+
$id = $this->getAlias().'.error_handler';
178+
$errorHandlerDefinition = $container->setDefinition($id, new Definition(ErrorHandler::class));
179+
$errorHandlerDefinition->setPublic(false)
180+
->setArguments(
181+
[
182+
new Reference('event_dispatcher'),
183+
$config['errors_handler']['internal_error_message'],
184+
$this->buildExceptionMap($config['errors_handler']['exceptions']),
185+
$config['errors_handler']['map_exceptions_to_parent'],
186+
]
187+
)
188+
->addMethodCall('setUserWarningClass', [$config['errors_handler']['exceptions']['types']['warnings']])
189+
->addMethodCall('setUserErrorClass', [$config['errors_handler']['exceptions']['types']['errors']])
190+
;
184191

185-
if (isset($config['definitions']['exceptions'])) {
186-
$errorHandlerDefinition
187-
->replaceArgument(2, $this->buildExceptionMap($config['definitions']['exceptions']))
188-
->addMethodCall('setUserWarningClass', [$config['definitions']['exceptions']['types']['warnings']])
189-
->addMethodCall('setUserErrorClass', [$config['definitions']['exceptions']['types']['errors']])
190-
->addMethodCall('setErrorFormatter', [$config['definitions']['error_formatter']])
192+
$errorHandlerListenerDefinition = $container->setDefinition(ErrorHandlerListener::class, new Definition(ErrorHandlerListener::class));
193+
$errorHandlerListenerDefinition->setPublic(true)
194+
->setArguments([new Reference($id), $config['errors_handler']['rethrow_internal_exceptions'], $config['errors_handler']['debug']])
195+
->addTag('kernel.event_listener', ['event' => Events::POST_EXECUTOR, 'method' => 'onPostExecutor'])
191196
;
197+
198+
if ($config['errors_handler']['log']) {
199+
$loggerServiceId = $config['errors_handler']['logger_service'];
200+
$invalidBehavior = ErrorLoggerListener::DEFAULT_LOGGER_SERVICE === $loggerServiceId ? ContainerInterface::NULL_ON_INVALID_REFERENCE : ContainerInterface::EXCEPTION_ON_INVALID_REFERENCE;
201+
$errorHandlerListenerDefinition = $container->setDefinition(ErrorLoggerListener::class, new Definition(ErrorLoggerListener::class));
202+
$errorHandlerListenerDefinition->setPublic(true)
203+
->setArguments([
204+
new Reference($loggerServiceId, $invalidBehavior),
205+
$config['errors_handler']['exceptions']['types']['errors'],
206+
$config['errors_handler']['exceptions']['types']['warnings'],
207+
]
208+
)
209+
->addTag('kernel.event_listener', ['event' => Events::ERROR_FORMATTING, 'method' => 'onErrorFormatting'])
210+
;
211+
}
192212
}
193213
}
194214

Error/ErrorHandler.php

Lines changed: 28 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -2,24 +2,24 @@
22

33
namespace Overblog\GraphQLBundle\Error;
44

5+
use GraphQL\Error\ClientAware;
6+
use GraphQL\Error\Debug;
57
use GraphQL\Error\Error as GraphQLError;
68
use GraphQL\Error\FormattedError;
79
use GraphQL\Error\UserError as GraphQLUserError;
810
use GraphQL\Executor\ExecutionResult;
9-
use Psr\Log\LoggerInterface;
10-
use Psr\Log\LogLevel;
11-
use Psr\Log\NullLogger;
11+
use Overblog\GraphQLBundle\Event\ErrorFormattingEvent;
12+
use Overblog\GraphQLBundle\Event\Events;
13+
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
1214

1315
class ErrorHandler
1416
{
1517
const DEFAULT_ERROR_MESSAGE = 'Internal server Error';
1618
const DEFAULT_USER_WARNING_CLASS = UserWarning::class;
1719
const DEFAULT_USER_ERROR_CLASS = UserError::class;
18-
/** callable */
19-
const DEFAULT_ERROR_FORMATTER = [FormattedError::class, 'createFromException'];
2020

21-
/** @var LoggerInterface */
22-
private $logger;
21+
/** @var EventDispatcherInterface */
22+
private $dispatcher;
2323

2424
/** @var string */
2525
private $internalErrorMessage;
@@ -33,19 +33,16 @@ class ErrorHandler
3333
/** @var string */
3434
private $userErrorClass = self::DEFAULT_USER_ERROR_CLASS;
3535

36-
/** @var callable|null */
37-
private $errorFormatter;
38-
3936
/** @var bool */
4037
private $mapExceptionsToParent;
4138

4239
public function __construct(
40+
EventDispatcherInterface $dispatcher,
4341
$internalErrorMessage = null,
44-
LoggerInterface $logger = null,
4542
array $exceptionMap = [],
4643
$mapExceptionsToParent = false
4744
) {
48-
$this->logger = (null === $logger) ? new NullLogger() : $logger;
45+
$this->dispatcher = $dispatcher;
4946
if (empty($internalErrorMessage)) {
5047
$internalErrorMessage = self::DEFAULT_ERROR_MESSAGE;
5148
}
@@ -68,43 +65,32 @@ public function setUserErrorClass($userErrorClass)
6865
return $this;
6966
}
7067

71-
public function setErrorFormatter(callable $errorFormatter = null)
72-
{
73-
$this->errorFormatter = $errorFormatter;
74-
75-
return $this;
76-
}
77-
78-
/**
79-
* @param \Exception|\Error $exception
80-
* @param string $errorLevel
81-
*/
82-
public function logException($exception, $errorLevel = LogLevel::ERROR)
83-
{
84-
$message = sprintf(
85-
'%s: %s[%d] (caught exception) at %s line %s.',
86-
get_class($exception),
87-
$exception->getMessage(),
88-
$exception->getCode(),
89-
$exception->getFile(),
90-
$exception->getLine()
91-
);
92-
93-
$this->logger->$errorLevel($message, ['exception' => $exception]);
94-
}
95-
96-
public function handleErrors(ExecutionResult $executionResult, $throwRawException = false)
68+
public function handleErrors(ExecutionResult $executionResult, $throwRawException = false, $debug = false)
9769
{
98-
$errorFormatter = $this->errorFormatter ? $this->errorFormatter : self::DEFAULT_ERROR_FORMATTER;
70+
$errorFormatter = $this->createErrorFormatter($debug);
9971
$executionResult->setErrorFormatter($errorFormatter);
100-
FormattedError::setInternalErrorMessage($this->internalErrorMessage);
10172
$exceptions = $this->treatExceptions($executionResult->errors, $throwRawException);
10273
$executionResult->errors = $exceptions['errors'];
10374
if (!empty($exceptions['extensions']['warnings'])) {
10475
$executionResult->extensions['warnings'] = array_map($errorFormatter, $exceptions['extensions']['warnings']);
10576
}
10677
}
10778

79+
private function createErrorFormatter($debug = false)
80+
{
81+
$debugMode = false;
82+
if ($debug) {
83+
$debugMode = Debug::INCLUDE_TRACE | Debug::INCLUDE_DEBUG_MESSAGE;
84+
}
85+
86+
return function (GraphQLError $error) use ($debugMode) {
87+
$event = new ErrorFormattingEvent($error, FormattedError::createFromException($error, $debugMode, $this->internalErrorMessage));
88+
$this->dispatcher->dispatch(Events::ERROR_FORMATTING, $event);
89+
90+
return $event->getFormattedError()->getArrayCopy();
91+
};
92+
}
93+
10894
/**
10995
* @param GraphQLError[] $errors
11096
* @param bool $throwRawException
@@ -145,18 +131,12 @@ private function treatExceptions(array $errors, $throwRawException)
145131
// user error
146132
if ($rawException instanceof $this->userErrorClass) {
147133
$treatedExceptions['errors'][] = $errorWithConvertedException;
148-
if ($rawException->getPrevious()) {
149-
$this->logException($rawException->getPrevious());
150-
}
151134
continue;
152135
}
153136

154137
// user warning
155138
if ($rawException instanceof $this->userWarningClass) {
156139
$treatedExceptions['extensions']['warnings'][] = $errorWithConvertedException;
157-
if ($rawException->getPrevious()) {
158-
$this->logException($rawException->getPrevious(), LogLevel::WARNING);
159-
}
160140
continue;
161141
}
162142

@@ -165,8 +145,6 @@ private function treatExceptions(array $errors, $throwRawException)
165145
throw $rawException;
166146
}
167147

168-
$this->logException($rawException, LogLevel::CRITICAL);
169-
170148
$treatedExceptions['errors'][] = $errorWithConvertedException;
171149
}
172150

@@ -208,8 +186,8 @@ private function flattenErrors(array $errors)
208186
*/
209187
private function convertException($rawException = null)
210188
{
211-
if (null === $rawException) {
212-
return;
189+
if (null === $rawException || $rawException instanceof ClientAware) {
190+
return $rawException;
213191
}
214192

215193
$errorClass = $this->findErrorClass($rawException);

Event/ErrorFormattingEvent.php

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
<?php
2+
3+
namespace Overblog\GraphQLBundle\Event;
4+
5+
use GraphQL\Error\Error;
6+
use Symfony\Component\EventDispatcher\Event;
7+
8+
final class ErrorFormattingEvent extends Event
9+
{
10+
/** @var Error */
11+
private $error;
12+
13+
/** @var \ArrayObject */
14+
private $formattedError;
15+
16+
public function __construct(Error $error, array $formattedError)
17+
{
18+
$this->error = $error;
19+
$this->formattedError = new \ArrayObject($formattedError);
20+
}
21+
22+
public function getError()
23+
{
24+
return $this->error;
25+
}
26+
27+
/**
28+
* @return \ArrayObject
29+
*/
30+
public function getFormattedError()
31+
{
32+
return $this->formattedError;
33+
}
34+
}

Event/Events.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,4 +7,5 @@ final class Events
77
const EXECUTOR_CONTEXT = 'graphql.executor.context';
88
const PRE_EXECUTOR = 'graphql.pre_executor';
99
const POST_EXECUTOR = 'graphql.post_executor';
10+
const ERROR_FORMATTING = 'graphql.error_formatting';
1011
}

Event/ExecutorContextEvent.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44

55
use Symfony\Component\EventDispatcher\Event;
66

7-
class ExecutorContextEvent extends Event
7+
final class ExecutorContextEvent extends Event
88
{
99
/** @var \ArrayObject */
1010
private $executorContext;

Event/ExecutorEvent.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
use GraphQL\Type\Schema;
66
use Symfony\Component\EventDispatcher\Event;
77

8-
class ExecutorEvent extends Event
8+
final class ExecutorEvent extends Event
99
{
1010
/** @var Schema */
1111
private $schema;

Event/ExecutorResultEvent.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
use GraphQL\Executor\ExecutionResult;
66
use Symfony\Component\EventDispatcher\Event;
77

8-
class ExecutorResultEvent extends Event
8+
final class ExecutorResultEvent extends Event
99
{
1010
/** @var ExecutionResult */
1111
private $result;

EventListener/ClassLoaderListener.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44

55
use Overblog\GraphQLBundle\Generator\TypeGenerator;
66

7-
class ClassLoaderListener
7+
final class ClassLoaderListener
88
{
99
private $typeGenerator;
1010

EventListener/DebugListener.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44

55
use Overblog\GraphQLBundle\Event\ExecutorResultEvent;
66

7-
class DebugListener
7+
final class DebugListener
88
{
99
/** @var float */
1010
private $startTime;

0 commit comments

Comments
 (0)