diff --git a/features/main/exception_to_status.feature b/features/main/exception_to_status.feature new file mode 100644 index 00000000000..203e13ea198 --- /dev/null +++ b/features/main/exception_to_status.feature @@ -0,0 +1,33 @@ +Feature: Using exception_to_status config + As an API developer + I can customize the status code returned if the application throws an exception + + @createSchema + @!mongodb + Scenario: Configure status code via the operation exceptionToStatus to map custom NotFound error to 404 + When I add "Content-Type" header equal to "application/ld+json" + And I send a "GET" request to "/dummy_exception_to_statuses/123" + Then the response status code should be 404 + And the response should be in JSON + And the header "Content-Type" should be equal to "application/ld+json; charset=utf-8" + + @!mongodb + Scenario: Configure status code via the resource exceptionToStatus to map custom NotFound error to 400 + When I add "Content-Type" header equal to "application/ld+json" + And I send a "PUT" request to "/dummy_exception_to_statuses/123" with body: + """ + { + "name": "black" + } + """ + Then the response status code should be 400 + And the response should be in JSON + And the header "Content-Type" should be equal to "application/ld+json; charset=utf-8" + + @!mongodb + Scenario: Configure status code via the config file to map FilterValidationException to 400 + When I add "Content-Type" header equal to "application/ld+json" + And I send a "GET" request to "/dummy_exception_to_statuses" + Then the response status code should be 400 + And the response should be in JSON + And the header "Content-Type" should be equal to "application/ld+json; charset=utf-8" diff --git a/src/Action/ExceptionAction.php b/src/Action/ExceptionAction.php index b5fd5f63ef9..bb6008894c3 100644 --- a/src/Action/ExceptionAction.php +++ b/src/Action/ExceptionAction.php @@ -96,6 +96,11 @@ public function __invoke($exception, Request $request): Response private function getOperationExceptionToStatus(Request $request): array { + // TODO: remove legacy layer in 3.0 + if ($request->attributes->has('_api_exception_to_status')) { + return $request->attributes->get('_api_exception_to_status'); + } + $attributes = RequestAttributesExtractor::extractAttributes($request); if ([] === $attributes || null === $this->resourceMetadataFactory) { diff --git a/src/Symfony/Bundle/DependencyInjection/ApiPlatformExtension.php b/src/Symfony/Bundle/DependencyInjection/ApiPlatformExtension.php index 7de2a4a2400..4e3d565872a 100644 --- a/src/Symfony/Bundle/DependencyInjection/ApiPlatformExtension.php +++ b/src/Symfony/Bundle/DependencyInjection/ApiPlatformExtension.php @@ -43,6 +43,7 @@ use ApiPlatform\Symfony\Validator\Metadata\Property\Restriction\PropertySchemaRestrictionMetadataInterface; use ApiPlatform\Symfony\Validator\ValidationGroupsGeneratorInterface; use Doctrine\Common\Annotations\Annotation; +use Doctrine\Persistence\ManagerRegistry; use phpDocumentor\Reflection\DocBlockFactoryInterface; use Ramsey\Uuid\Uuid; use Symfony\Component\Cache\Adapter\ArrayAdapter; @@ -657,6 +658,11 @@ private function registerDoctrineOrmConfiguration(ContainerBuilder $container, a return; } + // For older versions of doctrine bridge this allows autoconfiguration for filters + if (!$container->has(ManagerRegistry::class)) { + $container->setAlias(ManagerRegistry::class, 'doctrine'); + } + $container->registerForAutoconfiguration(QueryItemExtensionInterface::class) ->addTag('api_platform.doctrine.orm.query_extension.item'); $container->registerForAutoconfiguration(DoctrineQueryCollectionExtensionInterface::class) diff --git a/src/Symfony/Bundle/Resources/config/api.xml b/src/Symfony/Bundle/Resources/config/api.xml index a3d499e572f..527706249dd 100644 --- a/src/Symfony/Bundle/Resources/config/api.xml +++ b/src/Symfony/Bundle/Resources/config/api.xml @@ -175,5 +175,8 @@ + + api_platform.action.exception + diff --git a/src/Symfony/Bundle/Resources/config/legacy/api.xml b/src/Symfony/Bundle/Resources/config/legacy/api.xml index cceb903baa5..51fb6e717ba 100644 --- a/src/Symfony/Bundle/Resources/config/legacy/api.xml +++ b/src/Symfony/Bundle/Resources/config/legacy/api.xml @@ -103,7 +103,7 @@ api_platform.action.exception false - + diff --git a/src/Symfony/Bundle/Resources/config/symfony.xml b/src/Symfony/Bundle/Resources/config/symfony.xml index 871e3810c43..c7e5782c8ab 100644 --- a/src/Symfony/Bundle/Resources/config/symfony.xml +++ b/src/Symfony/Bundle/Resources/config/symfony.xml @@ -70,7 +70,7 @@ api_platform.action.exception false - + diff --git a/src/Symfony/EventListener/ErrorListener.php b/src/Symfony/EventListener/ErrorListener.php new file mode 100644 index 00000000000..c1b4e19f3d1 --- /dev/null +++ b/src/Symfony/EventListener/ErrorListener.php @@ -0,0 +1,42 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +declare(strict_types=1); + +namespace ApiPlatform\Symfony\EventListener; + +use ApiPlatform\Action\ExceptionAction; +use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\HttpKernel\EventListener\ErrorListener as SymfonyErrorListener; + +/** + * This error listener extends the Symfony one in order to add + * the `_api_operation` attribute when the request is duplicated. + * It will later be used to retrieve the exceptionToStatus from the operation ({@see ExceptionAction}). + */ +final class ErrorListener extends SymfonyErrorListener +{ + protected function duplicateRequest(\Throwable $exception, Request $request): Request + { + $dup = parent::duplicateRequest($exception, $request); + + if ($request->attributes->has('_api_operation')) { + $dup->attributes->set('_api_operation', $request->attributes->get('_api_operation')); + } + + // TODO: remove legacy layer in 3.0 + if ($request->attributes->has('_api_exception_to_status')) { + $dup->attributes->set('_api_exception_to_status', $request->attributes->get('_api_exception_to_status')); + } + + return $dup; + } +} diff --git a/src/Symfony/EventListener/ExceptionListener.php b/src/Symfony/EventListener/ExceptionListener.php index 8afdb5b4cea..6eb340981da 100644 --- a/src/Symfony/EventListener/ExceptionListener.php +++ b/src/Symfony/EventListener/ExceptionListener.php @@ -28,13 +28,16 @@ final class ExceptionListener { /** - * @var ErrorListener + * @phpstan-ignore-next-line legacy may not exist + * + * @var ErrorListener|LegacyExceptionListener */ private $exceptionListener; public function __construct($controller, LoggerInterface $logger = null, $debug = false, ErrorListener $errorListener = null) { - $this->exceptionListener = $errorListener ? new ErrorListener($controller, $logger, $debug) : new LegacyExceptionListener($controller, $logger, $debug); // @phpstan-ignore-line + // @phpstan-ignore-next-line legacy may not exist + $this->exceptionListener = $errorListener ?: new LegacyExceptionListener($controller, $logger, $debug); } public function onKernelException(ExceptionEvent $event): void @@ -48,7 +51,7 @@ public function onKernelException(ExceptionEvent $event): void return; } - $this->exceptionListener->onKernelException($event); + $this->exceptionListener->onKernelException($event); // @phpstan-ignore-line } } diff --git a/src/Symfony/Routing/ApiLoader.php b/src/Symfony/Routing/ApiLoader.php index 8d4dd76e488..ef03a34c136 100644 --- a/src/Symfony/Routing/ApiLoader.php +++ b/src/Symfony/Routing/ApiLoader.php @@ -336,6 +336,7 @@ private function addRoute(RouteCollection $routeCollection, string $resourceClas '_api_resource_class' => $resourceClass, '_api_identifiers' => $operation['identifiers'] ?? [], '_api_has_composite_identifier' => $operation['has_composite_identifier'] ?? true, + '_api_exception_to_status' => $operation['exception_to_status'] ?? $resourceMetadata->getAttribute('exception_to_status') ?? [], '_api_operation_name' => RouteNameGenerator::generate($operationName, $resourceShortName, $operationType), sprintf('_api_%s_operation_name', $operationType) => $operationName, ] + ($operation['defaults'] ?? []), diff --git a/src/Test/DoctrineMongoDbOdmTestCase.php b/src/Test/DoctrineMongoDbOdmTestCase.php index 71f613c9dd1..b2bec59a0f2 100644 --- a/src/Test/DoctrineMongoDbOdmTestCase.php +++ b/src/Test/DoctrineMongoDbOdmTestCase.php @@ -20,6 +20,7 @@ use Doctrine\ODM\MongoDB\Mapping\Driver\AnnotationDriver; use PHPUnit\Framework\TestCase; use Symfony\Component\Cache\Adapter\ArrayAdapter; + use function sys_get_temp_dir; /** diff --git a/tests/Core/Bridge/Symfony/Routing/ApiLoaderTest.php b/tests/Core/Bridge/Symfony/Routing/ApiLoaderTest.php index a38b5033507..accf0001be7 100644 --- a/tests/Core/Bridge/Symfony/Routing/ApiLoaderTest.php +++ b/tests/Core/Bridge/Symfony/Routing/ApiLoaderTest.php @@ -328,6 +328,7 @@ private function getRoute(string $path, string $controller, string $resourceClas '_api_has_composite_identifier' => false, sprintf('_api_%s_operation_name', $collection ? 'collection' : 'item') => $legacyOperationName, '_api_operation_name' => $operationName, + '_api_exception_to_status' => [], ] + $extraDefaults, $requirements, $options, diff --git a/tests/Fixtures/TestBundle/Entity/DummyExceptionToStatus.php b/tests/Fixtures/TestBundle/Entity/DummyExceptionToStatus.php new file mode 100644 index 00000000000..92c77d04635 --- /dev/null +++ b/tests/Fixtures/TestBundle/Entity/DummyExceptionToStatus.php @@ -0,0 +1,116 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +declare(strict_types=1); + +namespace ApiPlatform\Tests\Fixtures\TestBundle\Entity; + +use ApiPlatform\Core\Annotation\ApiFilter; +use ApiPlatform\Core\Annotation\ApiResource; +use ApiPlatform\Tests\Fixtures\TestBundle\Exception\NotFoundException; +use ApiPlatform\Tests\Fixtures\TestBundle\Filter\RequiredFilter; +use Doctrine\ORM\Mapping as ORM; +use Symfony\Component\HttpKernel\Exception\NotFoundHttpException; + +/** + * @ApiResource( + * itemOperations={ + * "get"={"exception_to_status"={NotFoundException::class=404}}, + * "put" + * }, + * collectionOperations={"get"}, + * exceptionToStatus={ + * NotFoundHttpException::class=400 + * } + * ) + * @ApiFilter(RequiredFilter::class) + * @ORM\Entity + */ +class DummyExceptionToStatus +{ + /** + * @var int|null The id + * + * @ORM\Column(type="integer") + * @ORM\Id + * @ORM\GeneratedValue(strategy="AUTO") + */ + private $id = null; + + /** + * @var string|null The dummy name + * + * @ORM\Column(nullable=true) + */ + private $name = null; + + /** + * @var string|null The dummy title + * + * @ORM\Column(nullable=true) + */ + private $title = null; + + /** + * @var string The dummy code + * + * @ORM\Column + */ + private $code; + + public function getId(): ?int + { + return $this->id; + } + + public function setId(int $id): self + { + $this->id = $id; + + return $this; + } + + public function getName(): ?string + { + return $this->name; + } + + public function setName(?string $name): self + { + $this->name = $name; + + return $this; + } + + public function getTitle(): ?string + { + return $this->title; + } + + public function setTitle(?string $title): self + { + $this->title = $title; + + return $this; + } + + public function getCode(): ?string + { + return $this->code; + } + + public function setCode(string $code): self + { + $this->code = $code; + + return $this; + } +} diff --git a/tests/Fixtures/TestBundle/Exception/NotFoundException.php b/tests/Fixtures/TestBundle/Exception/NotFoundException.php new file mode 100644 index 00000000000..b81db1bd329 --- /dev/null +++ b/tests/Fixtures/TestBundle/Exception/NotFoundException.php @@ -0,0 +1,22 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +declare(strict_types=1); + +namespace ApiPlatform\Tests\Fixtures\TestBundle\Exception; + +final class NotFoundException extends \Exception +{ + public function __construct(string $message = '', int $code = 0, ?\Throwable $previous = null) + { + parent::__construct($message, $code, $previous); + } +} diff --git a/tests/Fixtures/TestBundle/State/DummyExceptionToStatusProvider.php b/tests/Fixtures/TestBundle/State/DummyExceptionToStatusProvider.php new file mode 100644 index 00000000000..a6ed5e23aae --- /dev/null +++ b/tests/Fixtures/TestBundle/State/DummyExceptionToStatusProvider.php @@ -0,0 +1,26 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +declare(strict_types=1); + +namespace ApiPlatform\Tests\Fixtures\TestBundle\State; + +use ApiPlatform\Metadata\Operation; +use ApiPlatform\State\ProviderInterface; +use ApiPlatform\Tests\Fixtures\TestBundle\Exception\NotFoundException; + +class DummyExceptionToStatusProvider implements ProviderInterface +{ + public function provide(Operation $operation, array $uriVariables = [], array $context = []) + { + throw new NotFoundException(); + } +} diff --git a/tests/Fixtures/app/config/config_common.yml b/tests/Fixtures/app/config/config_common.yml index 21568e81d20..ffdcfaeff55 100644 --- a/tests/Fixtures/app/config/config_common.yml +++ b/tests/Fixtures/app/config/config_common.yml @@ -105,7 +105,7 @@ services: class: 'ApiPlatform\Tests\Fixtures\TestBundle\State\AttributeResourceProvider' public: false tags: - - { name: 'api_platform.state_provider' } + - { name: 'api_platform.state_provider' } ApiPlatform\Tests\Fixtures\TestBundle\State\DummyCollectionDtoProvider: class: 'ApiPlatform\Tests\Fixtures\TestBundle\State\DummyCollectionDtoProvider' @@ -113,6 +113,10 @@ services: tags: - { name: 'api_platform.state_provider' } + ApiPlatform\Tests\Fixtures\TestBundle\State\DummyExceptionToStatusProvider: + tags: + - { name: 'api_platform.state_provider' } + # related_questions.state_provider: # class: 'ApiPlatform\Tests\Fixtures\TestBundle\State\RelatedQuestionsProvider' # public: false @@ -127,84 +131,85 @@ services: tags: - { name: 'api_platform.state_processor' } - ApiPlatform\Tests\Fixtures\TestBundle\State\ContainNonResourceProvider: class: 'ApiPlatform\Tests\Fixtures\TestBundle\State\ContainNonResourceProvider' public: false tags: - - { name: 'api_platform.state_provider' } + - { name: 'api_platform.state_provider' } ApiPlatform\Tests\Fixtures\TestBundle\State\SerializableProvider: class: 'ApiPlatform\Tests\Fixtures\TestBundle\State\SerializableProvider' public: false tags: - - { name: 'api_platform.state_provider' } + - { name: 'api_platform.state_provider' } ApiPlatform\Tests\Fixtures\TestBundle\State\FakeProvider: class: 'ApiPlatform\Tests\Fixtures\TestBundle\State\FakeProvider' tags: - - { name: 'api_platform.state_provider' } + - { name: 'api_platform.state_provider' } ApiPlatform\Tests\Fixtures\TestBundle\State\CarProcessor: class: 'ApiPlatform\Tests\Fixtures\TestBundle\State\CarProcessor' tags: - - { name: 'api_platform.state_processor' } + - { name: 'api_platform.state_processor' } ApiPlatform\Tests\Fixtures\TestBundle\State\ResourceInterfaceImplementationProvider: class: 'ApiPlatform\Tests\Fixtures\TestBundle\State\ResourceInterfaceImplementationProvider' public: false tags: - - { name: 'api_platform.state_provider' } + - { name: 'api_platform.state_provider' } contain_non_resource.item_data_provider: class: 'ApiPlatform\Tests\Fixtures\TestBundle\DataProvider\ContainNonResourceItemDataProvider' public: false tags: - - { name: 'api_platform.item_data_provider' } + - { name: 'api_platform.item_data_provider' } serializable.item_data_provider: class: 'ApiPlatform\Tests\Fixtures\TestBundle\DataProvider\SerializableItemDataProvider' public: false tags: - - { name: 'api_platform.item_data_provider' } + - { name: 'api_platform.item_data_provider' } resource_interface.data_provider: class: 'ApiPlatform\Tests\Fixtures\TestBundle\DataProvider\ResourceInterfaceImplementationDataProvider' public: false tags: - - { name: 'api_platform.item_data_provider' } - - { name: 'api_platform.collection_data_provider' } + - { name: 'api_platform.item_data_provider' } + - { name: 'api_platform.collection_data_provider' } app.serializer.denormalizer.serializable_resource: class: 'ApiPlatform\Tests\Fixtures\TestBundle\Serializer\Denormalizer\SerializableResourceDenormalizer' public: false tags: - - { name: 'serializer.normalizer' } + - { name: 'serializer.normalizer' } app.serializer.denormalizer.related_dummy_plain_identifier: class: 'ApiPlatform\Tests\Fixtures\TestBundle\Serializer\Denormalizer\RelatedDummyPlainIdentifierDenormalizer' public: false arguments: ['@api_platform.iri_converter'] tags: - - { name: 'serializer.normalizer' } + - { name: 'serializer.normalizer' } app.serializer.denormalizer.dummy_plain_identifier: class: 'ApiPlatform\Tests\Fixtures\TestBundle\Serializer\Denormalizer\DummyPlainIdentifierDenormalizer' public: false arguments: ['@api_platform.iri_converter'] tags: - - { name: 'serializer.normalizer' } + - { name: 'serializer.normalizer' } app.name_converter: class: 'ApiPlatform\Tests\Fixtures\TestBundle\Serializer\NameConverter\CustomConverter' app.my_dummy_resource.property_filter: - parent: 'api_platform.serializer.property_filter' - tags: [ { name: 'api_platform.filter', id: 'my_dummy.property' } ] + parent: 'api_platform.serializer.property_filter' + tags: + - { name: 'api_platform.filter', id: 'my_dummy.property' } app.dummy_travel_resource.property_filter: parent: 'api_platform.serializer.property_filter' - tags: [ { name: 'api_platform.filter', id: 'dummy_travel.property' } ] + tags: + - { name: 'api_platform.filter', id: 'dummy_travel.property' } ApiPlatform\Tests\Fixtures\TestBundle\Filter\RequiredFilter: arguments: ['@doctrine'] @@ -216,19 +221,19 @@ services: ApiPlatform\Tests\Fixtures\TestBundle\Filter\RequiredAllowEmptyFilter: arguments: [ '@doctrine' ] - tags: [ 'api_platform.filter' ] + tags: ['api_platform.filter'] ApiPlatform\Tests\Fixtures\TestBundle\Filter\BoundsFilter: arguments: [ '@doctrine' ] - tags: [ 'api_platform.filter' ] + tags: ['api_platform.filter'] ApiPlatform\Tests\Fixtures\TestBundle\Filter\LengthFilter: arguments: [ '@doctrine' ] - tags: [ 'api_platform.filter' ] + tags: ['api_platform.filter'] ApiPlatform\Tests\Fixtures\TestBundle\Filter\PatternFilter: arguments: [ '@doctrine' ] - tags: [ 'api_platform.filter' ] + tags: ['api_platform.filter'] ApiPlatform\Tests\Fixtures\TestBundle\Controller\: resource: '../../TestBundle/Controller' @@ -238,15 +243,15 @@ services: ApiPlatform\Tests\Fixtures\TestBundle\Filter\EnumFilter: arguments: [ '@doctrine' ] - tags: [ 'api_platform.filter' ] + tags: ['api_platform.filter'] ApiPlatform\Tests\Fixtures\TestBundle\Filter\MultipleOfFilter: arguments: [ '@doctrine' ] - tags: [ 'api_platform.filter' ] + tags: ['api_platform.filter'] ApiPlatform\Tests\Fixtures\TestBundle\Filter\ArrayItemsFilter: arguments: [ '@doctrine' ] - tags: [ 'api_platform.filter' ] + tags: ['api_platform.filter'] app.config_dummy_resource.action: class: 'ApiPlatform\Tests\Fixtures\TestBundle\Action\ConfigCustom' @@ -254,37 +259,44 @@ services: public: true app.entity.filter.dummy_property.property: - parent: 'api_platform.serializer.property_filter' - tags: [ { name: 'api_platform.filter', id: 'dummy_property.property' } ] + parent: 'api_platform.serializer.property_filter' + tags: + - { name: 'api_platform.filter', id: 'dummy_property.property' } app.entity.filter.dummy_property.whitelist_property: - parent: 'api_platform.serializer.property_filter' + parent: 'api_platform.serializer.property_filter' arguments: [ 'whitelisted_properties', false, [foo, nameConverted] ] - tags: [ { name: 'api_platform.filter', id: 'dummy_property.whitelist_property' } ] + tags: + - { name: 'api_platform.filter', id: 'dummy_property.whitelist_property' } app.entity.filter.dummy_property.whitelist_nested_property: - parent: 'api_platform.serializer.property_filter' + parent: 'api_platform.serializer.property_filter' arguments: [ 'whitelisted_nested_properties', false, {foo: ~, group: [baz, qux]} ] - tags: [ { name: 'api_platform.filter', id: 'dummy_property.whitelisted_properties' } ] + tags: + - { name: 'api_platform.filter', id: 'dummy_property.whitelisted_properties' } app.entity.filter.dummy_group.group: - parent: 'api_platform.serializer.group_filter' - tags: [ { name: 'api_platform.filter', id: 'dummy_group.group' } ] + parent: 'api_platform.serializer.group_filter' + tags: + - { name: 'api_platform.filter', id: 'dummy_group.group' } app.entity.filter.dummy_group.override_group: - parent: 'api_platform.serializer.group_filter' + parent: 'api_platform.serializer.group_filter' arguments: [ 'override_groups', true ] - tags: [ { name: 'api_platform.filter', id: 'dummy_group.override_group' } ] + tags: + - { name: 'api_platform.filter', id: 'dummy_group.override_group' } app.entity.filter.dummy_group.whitelist_group: - parent: 'api_platform.serializer.group_filter' + parent: 'api_platform.serializer.group_filter' arguments: [ 'whitelisted_groups', false, ['dummy_foo', 'dummy_baz'] ] - tags: [ { name: 'api_platform.filter', id: 'dummy_group.whitelist_group' } ] + tags: + - { name: 'api_platform.filter', id: 'dummy_group.whitelist_group' } app.entity.filter.dummy_group.override_whitelist_group: - parent: 'api_platform.serializer.group_filter' + parent: 'api_platform.serializer.group_filter' arguments: [ 'override_whitelisted_groups', true, ['dummy_foo', 'dummy_baz'] ] - tags: [ { name: 'api_platform.filter', id: 'dummy_group.override_whitelist_group' } ] + tags: + - { name: 'api_platform.filter', id: 'dummy_group.override_whitelist_group' } logger: class: Psr\Log\NullLogger @@ -318,13 +330,13 @@ services: class: 'ApiPlatform\Tests\Fixtures\TestBundle\DataTransformer\CustomInputDtoDataTransformer' public: false tags: - - { name: 'api_platform.data_transformer' } + - { name: 'api_platform.data_transformer' } app.data_transformer.custom_output_dto: class: 'ApiPlatform\Tests\Fixtures\TestBundle\DataTransformer\CustomOutputDtoDataTransformer' public: false tags: - - { name: 'api_platform.data_transformer' } + - { name: 'api_platform.data_transformer' } app.data_transformer.custom_output_dto_fallback_same_class: class: 'ApiPlatform\Tests\Fixtures\TestBundle\DataTransformer\OutputDtoSameClassTransformer' @@ -336,43 +348,43 @@ services: class: 'ApiPlatform\Tests\Fixtures\TestBundle\DataTransformer\InputDtoDataTransformer' public: false tags: - - { name: 'api_platform.data_transformer' } + - { name: 'api_platform.data_transformer' } app.data_transformer.output_dto: class: 'ApiPlatform\Tests\Fixtures\TestBundle\DataTransformer\OutputDtoDataTransformer' public: false tags: - - { name: 'api_platform.data_transformer' } + - { name: 'api_platform.data_transformer' } app.data_transformer.dummy_dto_no_input_to_output_dto: class: 'ApiPlatform\Tests\Fixtures\TestBundle\DataTransformer\DummyDtoNoInputToOutputDtoDataTransformer' public: false tags: - - { name: 'api_platform.data_transformer' } + - { name: 'api_platform.data_transformer' } app.data_transformer.recover_password_input: class: 'ApiPlatform\Tests\Fixtures\TestBundle\DataTransformer\RecoverPasswordInputDataTransformer' public: false tags: - - { name: 'api_platform.data_transformer' } + - { name: 'api_platform.data_transformer' } app.data_transformer.recover_password_output: class: 'ApiPlatform\Tests\Fixtures\TestBundle\DataTransformer\RecoverPasswordOutputDataTransformer' public: false tags: - - { name: 'api_platform.data_transformer' } + - { name: 'api_platform.data_transformer' } app.data_transformer.initialize_input: class: 'ApiPlatform\Tests\Fixtures\TestBundle\DataTransformer\InitializeInputDataTransformer' public: false tags: - - { name: 'api_platform.data_transformer' } + - { name: 'api_platform.data_transformer' } app.messenger_handler.messenger_with_response: class: 'ApiPlatform\Tests\Fixtures\TestBundle\MessengerHandler\MessengerWithResponseHandler' public: false tags: - - { name: 'messenger.message_handler' } + - { name: 'messenger.message_handler' } app.graphql.query_resolver.dummy_custom_item: class: 'ApiPlatform\Tests\Fixtures\TestBundle\GraphQl\Resolver\DummyCustomQueryItemResolver' @@ -438,7 +450,7 @@ services: class: 'ApiPlatform\Tests\Fixtures\TestBundle\DataTransformer\RPCOutputDataTransformer' public: false tags: - - { name: 'api_platform.data_transformer' } + - { name: 'api_platform.data_transformer' } app.security.authentication_entrypoint: class: 'ApiPlatform\Tests\Fixtures\TestBundle\Security\AuthenticationEntryPoint' diff --git a/tests/Symfony/EventListener/ExceptionListenerTest.php b/tests/Symfony/EventListener/ExceptionListenerTest.php index afc5223240a..06d66ab5dff 100644 --- a/tests/Symfony/EventListener/ExceptionListenerTest.php +++ b/tests/Symfony/EventListener/ExceptionListenerTest.php @@ -14,11 +14,10 @@ namespace ApiPlatform\Tests\Symfony\EventListener; use ApiPlatform\Core\Tests\ProphecyTrait; +use ApiPlatform\Metadata\Get; use ApiPlatform\Symfony\EventListener\ExceptionListener; use PHPUnit\Framework\TestCase; -use Prophecy\Argument; use Symfony\Component\HttpFoundation\Request; -use Symfony\Component\HttpFoundation\Response; use Symfony\Component\HttpKernel\Event\ExceptionEvent; use Symfony\Component\HttpKernel\EventListener\ErrorListener; use Symfony\Component\HttpKernel\HttpKernelInterface; @@ -36,20 +35,23 @@ class ExceptionListenerTest extends TestCase public function testOnKernelException(Request $request) { $kernel = $this->prophesize(HttpKernelInterface::class); - $kernel->handle(Argument::type(Request::class), HttpKernelInterface::SUB_REQUEST, false)->willReturn(new Response())->shouldBeCalled(); - $listener = new ExceptionListener('foo:bar', null, false, class_exists(ErrorListener::class) ? $this->prophesize(ErrorListener::class)->reveal() : null); + $errorListener = class_exists(ErrorListener::class) ? $this->prophesize(ErrorListener::class) : null; + $event = new ExceptionEvent($kernel->reveal(), $request, \defined(HttpKernelInterface::class.'::MAIN_REQUEST') ? HttpKernelInterface::MAIN_REQUEST : HttpKernelInterface::MASTER_REQUEST, new \Exception()); - $listener->onKernelException($event); - $this->assertInstanceOf(Response::class, $event->getResponse()); + if ($errorListener) { + $errorListener->onKernelException($event)->shouldBeCalled(); + } + + $listener = new ExceptionListener('foo:bar', null, false, $errorListener ? $errorListener->reveal() : null); + $listener->onKernelException($event); } public function getRequest() { return [ - [new Request([], [], ['_api_resource_class' => 'Foo', '_api_collection_operation_name' => 'get'])], - [new Request([], [], ['_api_respond' => true])], + [new Request([], [], ['_api_resource_class' => 'Foo', '_api_operation' => new Get(), '_api_respond' => true])], ]; }