diff --git a/features/hydra/entrypoint.feature b/features/hydra/entrypoint.feature index b4b008583cc..b2b8f731d27 100644 --- a/features/hydra/entrypoint.feature +++ b/features/hydra/entrypoint.feature @@ -4,6 +4,7 @@ Feature: Entrypoint support I need to access to an entrypoint listing top-level resources Scenario: Retrieve the Entrypoint + When I add "Accept" header equal to "application/ld+json" When I send a "GET" request to "/" Then the response status code should be 200 And the response should be in JSON diff --git a/features/openapi/docs.feature b/features/openapi/docs.feature index 13b2cba199b..18f09998c8b 100644 --- a/features/openapi/docs.feature +++ b/features/openapi/docs.feature @@ -362,3 +362,9 @@ Feature: Documentation support And I send a "GET" request to "/docs" Then the response status code should be 200 And the header "Content-Type" should be equal to "application/vnd.openapi+yaml; charset=utf-8" + + Scenario: Retrieve the OpenAPI documentation + Given I add "Accept" header equal to "text/html" + And I send a "GET" request to "/" + Then the response status code should be 200 + And the header "Content-Type" should be equal to "text/html; charset=utf-8" diff --git a/src/Documentation/Action/EntrypointAction.php b/src/Documentation/Action/EntrypointAction.php index 85070b66ba2..ce619e4f52e 100644 --- a/src/Documentation/Action/EntrypointAction.php +++ b/src/Documentation/Action/EntrypointAction.php @@ -16,6 +16,7 @@ use ApiPlatform\Documentation\Entrypoint; use ApiPlatform\Metadata\Get; use ApiPlatform\Metadata\Resource\Factory\ResourceNameCollectionFactoryInterface; +use ApiPlatform\Metadata\Resource\ResourceNameCollection; use ApiPlatform\State\ProcessorInterface; use ApiPlatform\State\ProviderInterface; use Symfony\Component\HttpFoundation\Request; @@ -27,6 +28,8 @@ */ final class EntrypointAction { + private static ResourceNameCollection $resourceNameCollection; + public function __construct( private readonly ResourceNameCollectionFactoryInterface $resourceNameCollectionFactory, private readonly ProviderInterface $provider, @@ -35,12 +38,21 @@ public function __construct( ) { } - public function __invoke(Request $request = null) + public function __invoke(Request $request) { + static::$resourceNameCollection = $this->resourceNameCollectionFactory->create(); $context = ['request' => $request]; - $operation = new Get(outputFormats: $this->documentationFormats, read: true, serialize: true, class: Entrypoint::class, provider: fn () => new Entrypoint($this->resourceNameCollectionFactory->create())); + $request->attributes->set('_api_platform_disable_listeners', true); + $operation = new Get(outputFormats: $this->documentationFormats, read: true, serialize: true, class: Entrypoint::class, provider: [self::class, 'provide']); + $request->attributes->set('_api_operation', $operation); $body = $this->provider->provide($operation, [], $context); + $operation = $request->attributes->get('_api_operation'); return $this->processor->process($body, $operation, [], $context); } + + public static function provide(): Entrypoint + { + return new Entrypoint(static::$resourceNameCollection); + } } diff --git a/src/Symfony/Bundle/Resources/config/api.xml b/src/Symfony/Bundle/Resources/config/api.xml index 7f3d5324607..2230d6ea31e 100644 --- a/src/Symfony/Bundle/Resources/config/api.xml +++ b/src/Symfony/Bundle/Resources/config/api.xml @@ -96,7 +96,7 @@ - + diff --git a/src/Symfony/Bundle/Resources/config/legacy/events.xml b/src/Symfony/Bundle/Resources/config/legacy/events.xml index ef204daf222..09cd273d3be 100644 --- a/src/Symfony/Bundle/Resources/config/legacy/events.xml +++ b/src/Symfony/Bundle/Resources/config/legacy/events.xml @@ -10,6 +10,7 @@ %api_platform.formats% %api_platform.error_formats% %api_platform.docs_formats% + %api_platform.event_listeners_backward_compatibility_layer% diff --git a/src/Symfony/EventListener/AddFormatListener.php b/src/Symfony/EventListener/AddFormatListener.php index 75441e8e7d8..fa4c3990582 100644 --- a/src/Symfony/EventListener/AddFormatListener.php +++ b/src/Symfony/EventListener/AddFormatListener.php @@ -34,7 +34,7 @@ final class AddFormatListener { use OperationRequestInitiatorTrait; - public function __construct(private readonly Negotiator $negotiator, ResourceMetadataCollectionFactoryInterface $resourceMetadataCollectionFactory = null, private readonly array $formats = [], private readonly array $errorFormats = [], private readonly array $docsFormats = []) + public function __construct(private readonly Negotiator $negotiator, ResourceMetadataCollectionFactoryInterface $resourceMetadataCollectionFactory = null, private readonly array $formats = [], private readonly array $errorFormats = [], private readonly array $docsFormats = [], private readonly bool $eventsBackwardCompatibility = true) { $this->resourceMetadataCollectionFactory = $resourceMetadataCollectionFactory; } @@ -50,7 +50,7 @@ public function onKernelRequest(RequestEvent $event): void $request = $event->getRequest(); $operation = $this->initializeOperation($request); - if ('api_platform.symfony.main_controller' === $operation?->getController()) { + if ('api_platform.symfony.main_controller' === $operation?->getController() || ($this->eventsBackwardCompatibility && 'api_platform.action.entrypoint' === $request->attributes->get('_controller')) || $request->attributes->get('_api_platform_disable_listeners')) { return; } diff --git a/src/Symfony/EventListener/AddHeadersListener.php b/src/Symfony/EventListener/AddHeadersListener.php index 097ff41eab0..02663db3146 100644 --- a/src/Symfony/EventListener/AddHeadersListener.php +++ b/src/Symfony/EventListener/AddHeadersListener.php @@ -35,7 +35,7 @@ public function __construct(private readonly bool $etag = false, private readonl public function onKernelResponse(ResponseEvent $event): void { $request = $event->getRequest(); - if (!$request->isMethodCacheable()) { + if (!$request->isMethodCacheable() || $request->attributes->get('_api_platform_disable_listeners')) { return; } diff --git a/src/Symfony/EventListener/AddLinkHeaderListener.php b/src/Symfony/EventListener/AddLinkHeaderListener.php index 2a41c2d53ba..fecd341729f 100644 --- a/src/Symfony/EventListener/AddLinkHeaderListener.php +++ b/src/Symfony/EventListener/AddLinkHeaderListener.php @@ -49,7 +49,7 @@ public function onKernelResponse(ResponseEvent $event): void $operation = $this->initializeOperation($request); // API Platform 3.2 has a MainController where everything is handled by processors/providers - if ('api_platform.symfony.main_controller' === $operation?->getController() || $this->isPreflightRequest($request)) { + if ('api_platform.symfony.main_controller' === $operation?->getController() || $this->isPreflightRequest($request) || $request->attributes->get('_api_platform_disable_listeners')) { return; } diff --git a/src/Symfony/EventListener/AddTagsListener.php b/src/Symfony/EventListener/AddTagsListener.php index a3970c77f85..1281ab2bcb8 100644 --- a/src/Symfony/EventListener/AddTagsListener.php +++ b/src/Symfony/EventListener/AddTagsListener.php @@ -58,6 +58,7 @@ public function onKernelResponse(ResponseEvent $event): void !$request->isMethodCacheable() || !$response->isCacheable() || (!$attributes = RequestAttributesExtractor::extractAttributes($request)) + || $request->attributes->get('_api_platform_disable_listeners') ) { return; } diff --git a/src/Symfony/EventListener/DenyAccessListener.php b/src/Symfony/EventListener/DenyAccessListener.php index 4a2b0b6ca21..fdaa9f9a881 100644 --- a/src/Symfony/EventListener/DenyAccessListener.php +++ b/src/Symfony/EventListener/DenyAccessListener.php @@ -62,7 +62,7 @@ public function onSecurityPostValidation(ViewEvent $event): void */ private function checkSecurity(Request $request, string $attribute, array $extraVariables = []): void { - if (!$this->resourceAccessChecker || !$attributes = RequestAttributesExtractor::extractAttributes($request)) { + if ($request->attributes->get('_api_platform_disable_listeners') || !$this->resourceAccessChecker || !$attributes = RequestAttributesExtractor::extractAttributes($request)) { return; } diff --git a/src/Symfony/EventListener/DeserializeListener.php b/src/Symfony/EventListener/DeserializeListener.php index bdde94c2c3b..e4349f899d0 100644 --- a/src/Symfony/EventListener/DeserializeListener.php +++ b/src/Symfony/EventListener/DeserializeListener.php @@ -70,6 +70,7 @@ public function onKernelRequest(RequestEvent $event): void || $request->isMethodSafe() || !($attributes = RequestAttributesExtractor::extractAttributes($request)) || !$attributes['receive'] + || $request->attributes->get('_api_platform_disable_listeners') ) { return; } diff --git a/src/Symfony/EventListener/QueryParameterValidateListener.php b/src/Symfony/EventListener/QueryParameterValidateListener.php index 0adda2e6ac9..ea83890bf95 100644 --- a/src/Symfony/EventListener/QueryParameterValidateListener.php +++ b/src/Symfony/EventListener/QueryParameterValidateListener.php @@ -47,6 +47,7 @@ public function onKernelRequest(RequestEvent $event): void !$request->isMethodSafe() || !($attributes = RequestAttributesExtractor::extractAttributes($request)) || 'GET' !== $request->getMethod() + || $request->attributes->get('_api_platform_disable_listeners') ) { return; } diff --git a/src/Symfony/EventListener/ReadListener.php b/src/Symfony/EventListener/ReadListener.php index 3ba63f0d2db..5e41061aa5e 100644 --- a/src/Symfony/EventListener/ReadListener.php +++ b/src/Symfony/EventListener/ReadListener.php @@ -60,7 +60,7 @@ public function onKernelRequest(RequestEvent $event): void $request = $event->getRequest(); $operation = $this->initializeOperation($request); - if ('api_platform.symfony.main_controller' === $operation?->getController()) { + if ('api_platform.symfony.main_controller' === $operation?->getController() || $request->attributes->get('_api_platform_disable_listeners')) { return; } diff --git a/src/Symfony/EventListener/RespondListener.php b/src/Symfony/EventListener/RespondListener.php index c1ebc0e667c..cdf2fe0076b 100644 --- a/src/Symfony/EventListener/RespondListener.php +++ b/src/Symfony/EventListener/RespondListener.php @@ -54,7 +54,7 @@ public function onKernelView(ViewEvent $event): void $controllerResult = $event->getControllerResult(); $operation = $this->initializeOperation($request); - if ('api_platform.symfony.main_controller' === $operation?->getController()) { + if ('api_platform.symfony.main_controller' === $operation?->getController() || $request->attributes->get('_api_platform_disable_listeners')) { return; } diff --git a/src/Symfony/EventListener/SerializeListener.php b/src/Symfony/EventListener/SerializeListener.php index 71c9510713e..72796c4b793 100644 --- a/src/Symfony/EventListener/SerializeListener.php +++ b/src/Symfony/EventListener/SerializeListener.php @@ -73,7 +73,7 @@ public function onKernelView(ViewEvent $event): void $operation = $this->initializeOperation($request); - if ('api_platform.symfony.main_controller' === $operation?->getController()) { + if ('api_platform.symfony.main_controller' === $operation?->getController() || $request->attributes->get('_api_platform_disable_listeners')) { return; } diff --git a/src/Symfony/EventListener/ValidateListener.php b/src/Symfony/EventListener/ValidateListener.php index 402f620926e..6a69d6f45fe 100644 --- a/src/Symfony/EventListener/ValidateListener.php +++ b/src/Symfony/EventListener/ValidateListener.php @@ -46,7 +46,7 @@ public function onKernelView(ViewEvent $event): void $controllerResult = $event->getControllerResult(); $request = $event->getRequest(); $operation = $this->initializeOperation($request); - if ('api_platform.symfony.main_controller' === $operation?->getController()) { + if ('api_platform.symfony.main_controller' === $operation?->getController() || $request->attributes->get('_api_platform_disable_listeners')) { return; } diff --git a/src/Symfony/EventListener/WriteListener.php b/src/Symfony/EventListener/WriteListener.php index 0be49176c9b..08c5db56ae5 100644 --- a/src/Symfony/EventListener/WriteListener.php +++ b/src/Symfony/EventListener/WriteListener.php @@ -60,7 +60,7 @@ public function onKernelView(ViewEvent $event): void $operation = $this->initializeOperation($request); // API Platform 3.2 has a MainController where everything is handled by processors/providers - if ('api_platform.symfony.main_controller' === $operation?->getController()) { + if ('api_platform.symfony.main_controller' === $operation?->getController() || $request->attributes->get('_api_platform_disable_listeners')) { return; } diff --git a/tests/Symfony/Bundle/Twig/ApiPlatformProfilerPanelTest.php b/tests/Symfony/Bundle/Twig/ApiPlatformProfilerPanelTest.php index 11ae553f35a..27636deb23b 100644 --- a/tests/Symfony/Bundle/Twig/ApiPlatformProfilerPanelTest.php +++ b/tests/Symfony/Bundle/Twig/ApiPlatformProfilerPanelTest.php @@ -57,6 +57,11 @@ protected function tearDown(): void parent::tearDown(); } + /** + * TODO: remove openapiContext to get rid of the legacy. + * + * @group legacy + */ public function testDebugBarContentNotResourceClass(): void { $client = static::createClient(); @@ -94,6 +99,11 @@ public function testDebugBarContent(): void $this->assertSame('mongodb' === $this->env ? DocumentDummy::class : Dummy::class, $block->filterXPath('//div[@class="sf-toolbar-info-piece"][./b[contains(., "Resource Class")]]/span')->html()); } + /** + * TODO: remove openapiContext to get rid of the legacy. + * + * @group legacy + */ public function testProfilerGeneralLayoutNotResourceClass(): void { $client = static::createClient();