diff --git a/src/Error/ErrorHandler.php b/src/Error/ErrorHandler.php index eff4dd7b3..02940aa2a 100644 --- a/src/Error/ErrorHandler.php +++ b/src/Error/ErrorHandler.php @@ -7,6 +7,7 @@ use Closure; use Error; use Exception; +use GraphQL\Error\ClientAware; use GraphQL\Error\DebugFlag; use GraphQL\Error\Error as GraphQLError; use GraphQL\Error\FormattedError; @@ -120,6 +121,15 @@ private function treatExceptions(array $errors, bool $throwRawException): array continue; } + // client-safe exception (e.g. a validation error): it is not an + // internal exception, so it must be formatted like a user error + // instead of being rethrown when rethrow_internal_exceptions is on. + if ($rawException instanceof ClientAware && $rawException->isClientSafe()) { + $treatedExceptions['errors'][] = $errorWithConvertedException; + + continue; + } + // if is a catch exception wrapped in Error if ($throwRawException) { throw $rawException; diff --git a/tests/Error/ErrorHandlerTest.php b/tests/Error/ErrorHandlerTest.php index ec715afad..91d06111d 100644 --- a/tests/Error/ErrorHandlerTest.php +++ b/tests/Error/ErrorHandlerTest.php @@ -16,10 +16,14 @@ use Overblog\GraphQLBundle\Error\UserError; use Overblog\GraphQLBundle\Error\UserErrors; use Overblog\GraphQLBundle\Error\UserWarning; +use Overblog\GraphQLBundle\Validator\Exception\ArgumentsValidationException; use PHPUnit\Framework\Attributes\DataProvider; use PHPUnit\Framework\TestCase; use Symfony\Component\EventDispatcher\EventDispatcherInterface; +use Symfony\Component\Validator\ConstraintViolationList; +use Symfony\Component\Validator\Validation; +use function class_exists; use function is_array; use function is_string; use function sprintf; @@ -146,6 +150,34 @@ public function testMaskErrorWithWrappedUserErrorAndThrowExceptionSetToTrue(): v $this->assertSame($expected, $executionResult->toArray()); } + public function testMaskErrorWithWrappedClientSafeExceptionAndThrowExceptionSetToTrue(): void + { + if (!class_exists(Validation::class)) { + $this->markTestSkipped('Symfony validator component is not installed'); + } + + // A client-safe exception (e.g. a validation error) is not internal, so it + // must be formatted like a user error, not rethrown. See #1194. + $executionResult = new ExecutionResult( + null, + [ + new GraphQLError('Error with wrapped validation error', null, null, [], null, new ArgumentsValidationException(new ConstraintViolationList())), + ] + ); + + $this->errorHandler->handleErrors($executionResult, true); + + $expected = [ + 'errors' => [ + [ + 'message' => 'Error with wrapped validation error', + ], + ], + ]; + + $this->assertSame($expected, $executionResult->toArray()); + } + public function testDebugEnabled(): void { try {