diff --git a/packages/open-api/EventListener/ResponseApiExceptionListener.php b/packages/open-api/EventListener/ResponseApiExceptionListener.php index 1bb25d56..8e4636b1 100644 --- a/packages/open-api/EventListener/ResponseApiExceptionListener.php +++ b/packages/open-api/EventListener/ResponseApiExceptionListener.php @@ -9,26 +9,18 @@ use Draw\Component\OpenApi\Schema\Response; use Draw\Component\OpenApi\Schema\Schema; use Symfony\Component\DependencyInjection\Attribute\TaggedIterator; -use Symfony\Component\EventDispatcher\EventSubscriberInterface; +use Symfony\Component\EventDispatcher\Attribute\AsEventListener; use Symfony\Component\HttpFoundation\JsonResponse; use Symfony\Component\HttpKernel\Event\ExceptionEvent; use Symfony\Component\Validator\ConstraintViolation; use Symfony\Component\Validator\ConstraintViolationInterface; use Symfony\Component\Validator\ConstraintViolationList; -final class ResponseApiExceptionListener implements EventSubscriberInterface +final class ResponseApiExceptionListener { - public static function getSubscribedEvents(): array - { - return [ - ExceptionEvent::class => ['onKernelException', 255], - PreDumpRootSchemaEvent::class => ['addErrorDefinition'], - ]; - } - public function __construct( /** - * @var ErrorToHttpCodeConverterInterface[] + * @var iterable */ #[TaggedIterator(ErrorToHttpCodeConverterInterface::class)] private iterable $errorToHttpCodeConverters = [], @@ -38,6 +30,7 @@ public function __construct( $this->errorToHttpCodeConverters ??= new ConfigurableErrorToHttpCodeConverter(); } + #[AsEventListener] public function addErrorDefinition(PreDumpRootSchemaEvent $event): void { $root = $event->getSchema(); @@ -88,6 +81,7 @@ public function addErrorDefinition(PreDumpRootSchemaEvent $event): void } } + #[AsEventListener(priority: 255)] public function onKernelException(ExceptionEvent $event): void { $request = $event->getRequest(); @@ -108,9 +102,7 @@ public function onKernelException(ExceptionEvent $event): void $data[$this->violationKey] = $this->getConstraintViolationData($error); } - if ($this->debug) { - $data['detail'] = $this->getExceptionDetail($error); - } + $data['detail'] = $this->getExceptionDetail($error); $event->setResponse( new JsonResponse( @@ -143,10 +135,7 @@ private function getConstraintViolationData(ConstraintViolationListException $ex return $errors; } - /** - * @return mixed - */ - private function getConstraintPayload(ConstraintViolationInterface $constraintViolation) + private function getConstraintPayload(ConstraintViolationInterface $constraintViolation): mixed { if (!$constraintViolation instanceof ConstraintViolation) { return null; @@ -173,10 +162,10 @@ private function getExceptionDetail(\Throwable $e): array foreach (explode("\n", $e->getTraceAsString()) as $line) { $result['stack'][] = $line; } + } - if ($previous = $e->getPrevious()) { - $result['previous'] = $this->getExceptionDetail($previous); - } + if ($previous = $e->getPrevious()) { + $result['previous'] = $this->getExceptionDetail($previous); } return $result; diff --git a/packages/open-api/Tests/EventListener/ResponseApiExceptionListenerTest.php b/packages/open-api/Tests/EventListener/ResponseApiExceptionListenerTest.php index 5b943631..ca9d3dcd 100644 --- a/packages/open-api/Tests/EventListener/ResponseApiExceptionListenerTest.php +++ b/packages/open-api/Tests/EventListener/ResponseApiExceptionListenerTest.php @@ -11,7 +11,6 @@ use Draw\Component\OpenApi\Schema\Root; use PHPUnit\Framework\Attributes\CoversClass; use PHPUnit\Framework\TestCase; -use Symfony\Component\EventDispatcher\EventSubscriberInterface; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Response; use Symfony\Component\HttpKernel\Event\ExceptionEvent; @@ -39,7 +38,9 @@ protected function setUp(): void $this->httpKernel = $this->createMock(HttpKernelInterface::class), $this->request = $this->createMock(Request::class), HttpKernelInterface::MAIN_REQUEST, - $this->exception = $this->createMock(\Exception::class) + $this->exception = new \Exception( + previous: new \Exception() + ) ); $this->request @@ -48,25 +49,6 @@ protected function setUp(): void ->willReturn('json'); } - public function testConstruct(): void - { - static::assertInstanceOf( - EventSubscriberInterface::class, - $this->object - ); - } - - public function testSubscribedEvents(): void - { - static::assertSame( - [ - ExceptionEvent::class => ['onKernelException', 255], - PreDumpRootSchemaEvent::class => ['addErrorDefinition'], - ], - $this->object::getSubscribedEvents() - ); - } - /** * todo Improve test asser on this. */ @@ -135,23 +117,45 @@ public function testOnKernelExceptionJsonResponse(): void public function testOnKernelExceptionDefaultDebugFalse(): void { + $exceptionDetail = json_decode( + $this->onKernelException()->getContent(), + true, + 512, + \JSON_THROW_ON_ERROR + )['detail']; + static::assertArrayNotHasKey( - 'detail', - json_decode($this->onKernelException()->getContent(), true, 512, \JSON_THROW_ON_ERROR) + 'stack', + $exceptionDetail ); } public function testOnKernelExceptionDebugFalse(): void { - static::assertArrayNotHasKey( - 'detail', - json_decode( - $this->onKernelException( - new ResponseApiExceptionListener(debug: false) - )->getContent(), - true, - 512, - \JSON_THROW_ON_ERROR + $exceptionDetail = json_decode( + $this->onKernelException( + new ResponseApiExceptionListener(debug: false) + )->getContent(), + true, + 512, + \JSON_THROW_ON_ERROR + )['detail']; + + $expectedKeys = ['class', 'message', 'code', 'file', 'line', 'previous']; + + static::assertEmpty( + $extraKeys = array_diff(array_keys($exceptionDetail), $expectedKeys), + sprintf( + 'Unexpected keys: %s', + implode(', ', $extraKeys) + ) + ); + + static::assertEmpty( + $missingKeys = array_diff($expectedKeys, array_keys($exceptionDetail)), + sprintf( + 'Missing keys: %s', + implode(', ', $missingKeys) ) ); } diff --git a/tests/OpenApi/EventListener/ResponseApiExceptionListenerTest.php b/tests/OpenApi/EventListener/ResponseApiExceptionListenerTest.php new file mode 100644 index 00000000..ad6dd196 --- /dev/null +++ b/tests/OpenApi/EventListener/ResponseApiExceptionListenerTest.php @@ -0,0 +1,29 @@ +assertEventListenersRegistered( + $this->object::class, + [ + 'kernel.exception' => ['onKernelException'], + PreDumpRootSchemaEvent::class => ['addErrorDefinition'], + ] + ); + } +}