From 9ab885a77b6f54ca277aa9d3a564d130403b71cc Mon Sep 17 00:00:00 2001 From: soyuka Date: Fri, 6 Sep 2024 16:27:44 +0200 Subject: [PATCH] fix(laravel): docs _format and open swagger ui --- .../Action/DocumentationAction.php | 1 - src/Laravel/ApiPlatformProvider.php | 38 ++++-- .../Controller/DocumentationController.php | 122 ++++++++++++++++++ .../Controller/EntrypointController.php | 74 +++++++++++ src/Laravel/State/SwaggerUiProvider.php | 2 + src/Laravel/Tests/AuthTest.php | 2 +- src/Laravel/Tests/DocsTest.php | 52 ++++++++ src/Laravel/Tests/JsonApiTest.php | 2 +- src/Laravel/config/api-platform.php | 4 + ...rmatsResourceMetadataCollectionFactory.php | 8 +- .../Provider/ContentNegotiationProvider.php | 7 +- .../EventListener/SwaggerUiListener.php | 39 ------ .../EventListener/SwaggerUiListenerTest.php | 59 --------- 13 files changed, 288 insertions(+), 122 deletions(-) create mode 100644 src/Laravel/Controller/DocumentationController.php create mode 100644 src/Laravel/Controller/EntrypointController.php create mode 100644 src/Laravel/Tests/DocsTest.php delete mode 100644 src/Symfony/Bundle/EventListener/SwaggerUiListener.php delete mode 100644 tests/Symfony/Bundle/EventListener/SwaggerUiListenerTest.php diff --git a/src/Documentation/Action/DocumentationAction.php b/src/Documentation/Action/DocumentationAction.php index e40cbbfc36f..e507041b6a7 100644 --- a/src/Documentation/Action/DocumentationAction.php +++ b/src/Documentation/Action/DocumentationAction.php @@ -98,7 +98,6 @@ class: OpenApi::class, ); if ('html' === $format) { - // TODO: support laravel this bounds Documentation with Symfony so it's not perfect $operation = $operation->withProcessor('api_platform.swagger_ui.processor')->withWrite(true); } diff --git a/src/Laravel/ApiPlatformProvider.php b/src/Laravel/ApiPlatformProvider.php index 1a1b7e2c0a3..16bf11721c1 100644 --- a/src/Laravel/ApiPlatformProvider.php +++ b/src/Laravel/ApiPlatformProvider.php @@ -13,8 +13,6 @@ namespace ApiPlatform\Laravel; -use ApiPlatform\Documentation\Action\DocumentationAction; -use ApiPlatform\Documentation\Action\EntrypointAction; use ApiPlatform\GraphQl\Error\ErrorHandler as GraphQlErrorHandler; use ApiPlatform\GraphQl\Error\ErrorHandlerInterface; use ApiPlatform\GraphQl\Executor; @@ -71,6 +69,8 @@ use ApiPlatform\JsonSchema\SchemaFactoryInterface; use ApiPlatform\Laravel\ApiResource\Error; use ApiPlatform\Laravel\Controller\ApiPlatformController; +use ApiPlatform\Laravel\Controller\DocumentationController; +use ApiPlatform\Laravel\Controller\EntrypointController; use ApiPlatform\Laravel\Eloquent\Extension\FilterQueryExtension; use ApiPlatform\Laravel\Eloquent\Extension\QueryExtensionInterface; use ApiPlatform\Laravel\Eloquent\Filter\FilterInterface as EloquentFilterInterface; @@ -317,6 +317,11 @@ public function register(): void $this->app->singleton(ResourceMetadataCollectionFactoryInterface::class, function (Application $app) { /** @var ConfigRepository */ $config = $app['config']; + $formats = $config->get('api-platform.formats'); + + if ($config->get('api-platform.swagger_ui.enabled', false) && !isset($formats['html'])) { + $formats['html'] = ['text/html']; + } return new CacheResourceCollectionMetadataFactory( new EloquentResourceCollectionMetadataFactory( @@ -356,7 +361,7 @@ public function register(): void ) ) ), - $config->get('api-platform.formats'), + $formats, $config->get('api-platform.patch_formats'), ) ) @@ -417,7 +422,10 @@ public function register(): void }); $this->app->singleton(SwaggerUiProvider::class, function (Application $app) { - return new SwaggerUiProvider($app->make(ReadProvider::class), $app->make(OpenApiFactoryInterface::class)); + /** @var ConfigRepository */ + $config = $app['config']; + + return new SwaggerUiProvider($app->make(ReadProvider::class), $app->make(OpenApiFactoryInterface::class), $config->get('api-platform.swagger_ui.enabled', false)); }); $this->app->singleton(ValidateProvider::class, function (Application $app) { @@ -469,9 +477,14 @@ public function register(): void $this->app->tag([RemoveProcessor::class, PersistProcessor::class], ProcessorInterface::class); $this->app->singleton(CallableProcessor::class, function (Application $app) { + /** @var ConfigRepository */ + $config = $app['config']; $tagged = iterator_to_array($app->tagged(ProcessorInterface::class)); - // TODO: tag SwaggerUiProcessor instead? - $tagged['api_platform.swagger_ui.processor'] = $app->make(SwaggerUiProcessor::class); + + if ($config->get('api-platform.swagger_ui.enabled', false)) { + // TODO: tag SwaggerUiProcessor instead? + $tagged['api_platform.swagger_ui.processor'] = $app->make(SwaggerUiProcessor::class); + } return new CallableProcessor(new ServiceLocator($tagged)); }); @@ -621,18 +634,18 @@ public function register(): void return new Options(title: $config->get('api-platform.title') ?? ''); }); - $this->app->singleton(DocumentationAction::class, function (Application $app) { + $this->app->singleton(DocumentationController::class, function (Application $app) { /** @var ConfigRepository */ $config = $app['config']; - return new DocumentationAction($app->make(ResourceNameCollectionFactoryInterface::class), $config->get('api-platform.title') ?? '', $config->get('api-platform.description') ?? '', $config->get('api-platform.version') ?? '', $app->make(OpenApiFactoryInterface::class), $app->make(ProviderInterface::class), $app->make(ProcessorInterface::class), $app->make(Negotiator::class), $config->get('api-platform.docs_formats')); + return new DocumentationController($app->make(ResourceNameCollectionFactoryInterface::class), $config->get('api-platform.title') ?? '', $config->get('api-platform.description') ?? '', $config->get('api-platform.version') ?? '', $app->make(OpenApiFactoryInterface::class), $app->make(ProviderInterface::class), $app->make(ProcessorInterface::class), $app->make(Negotiator::class), $config->get('api-platform.docs_formats'), $config->get('api-platform.swagger_ui.enabled', false)); }); - $this->app->singleton(EntrypointAction::class, function (Application $app) { + $this->app->singleton(EntrypointController::class, function (Application $app) { /** @var ConfigRepository */ $config = $app['config']; - return new EntrypointAction($app->make(ResourceNameCollectionFactoryInterface::class), $app->make(ProviderInterface::class), $app->make(ProcessorInterface::class), $config->get('api-platform.docs_formats')); + return new EntrypointController($app->make(ResourceNameCollectionFactoryInterface::class), $app->make(ProviderInterface::class), $app->make(ProcessorInterface::class), $config->get('api-platform.docs_formats')); }); $this->app->singleton(Pagination::class, function (Application $app) { @@ -1133,9 +1146,8 @@ public function boot(ResourceNameCollectionFactoryInterface $resourceNameCollect $route = new Route(['GET'], $prefix.'/contexts/{shortName?}{_format?}', [ContextAction::class, '__invoke']); $route->name('api_jsonld_context')->middleware(ApiPlatformMiddleware::class); $routeCollection->add($route); - // Maybe that we can alias Symfony Request to Laravel Request within the provider ? $route = new Route(['GET'], $prefix.'/docs{_format?}', function (Request $request, Application $app) { - $documentationAction = $app->make(DocumentationAction::class); + $documentationAction = $app->make(DocumentationController::class); return $documentationAction->__invoke($request); }); @@ -1143,7 +1155,7 @@ public function boot(ResourceNameCollectionFactoryInterface $resourceNameCollect $routeCollection->add($route); $route = new Route(['GET'], $prefix.'/{index?}{_format?}', function (Request $request, Application $app) { - $entrypointAction = $app->make(EntrypointAction::class); + $entrypointAction = $app->make(EntrypointController::class); return $entrypointAction->__invoke($request); }); diff --git a/src/Laravel/Controller/DocumentationController.php b/src/Laravel/Controller/DocumentationController.php new file mode 100644 index 00000000000..0b3b1809b74 --- /dev/null +++ b/src/Laravel/Controller/DocumentationController.php @@ -0,0 +1,122 @@ + + * + * 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\Laravel\Controller; + +use ApiPlatform\Documentation\Documentation; +use ApiPlatform\Metadata\Get; +use ApiPlatform\Metadata\Resource\Factory\ResourceNameCollectionFactoryInterface; +use ApiPlatform\Metadata\Util\ContentNegotiationTrait; +use ApiPlatform\OpenApi\Factory\OpenApiFactoryInterface; +use ApiPlatform\OpenApi\OpenApi; +use ApiPlatform\OpenApi\Serializer\ApiGatewayNormalizer; +use ApiPlatform\OpenApi\Serializer\LegacyOpenApiNormalizer; +use ApiPlatform\OpenApi\Serializer\OpenApiNormalizer; +use ApiPlatform\State\ProcessorInterface; +use ApiPlatform\State\ProviderInterface; +use Negotiation\Negotiator; +use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\HttpFoundation\Response; + +/** + * Generates the API documentation. + * + * @author Amrouche Hamza + */ +final class DocumentationController +{ + use ContentNegotiationTrait; + + /** + * @param array $documentationFormats + * @param ProviderInterface $provider + * @param ProcessorInterface $processor + */ + public function __construct( + private readonly ResourceNameCollectionFactoryInterface $resourceNameCollectionFactory, + private readonly string $title = '', + private readonly string $description = '', + private readonly string $version = '', + private readonly ?OpenApiFactoryInterface $openApiFactory = null, + private readonly ?ProviderInterface $provider = null, + private readonly ?ProcessorInterface $processor = null, + ?Negotiator $negotiator = null, + private readonly array $documentationFormats = [OpenApiNormalizer::JSON_FORMAT => ['application/vnd.openapi+json'], OpenApiNormalizer::FORMAT => ['application/json']], + private readonly bool $swaggerUiEnabled = true, + ) { + $this->negotiator = $negotiator ?? new Negotiator(); + } + + public function __invoke(Request $request): Response + { + $context = [ + 'api_gateway' => $request->query->getBoolean(ApiGatewayNormalizer::API_GATEWAY), + 'base_url' => $request->getBaseUrl(), + 'spec_version' => (string) $request->query->get(LegacyOpenApiNormalizer::SPEC_VERSION), + ]; + $request->attributes->set('_api_normalization_context', $request->attributes->get('_api_normalization_context', []) + $context); + // We want to find the format early on, this code is also executed later on by the ContentNegotiationProvider. + $this->addRequestFormats($request, $this->documentationFormats); + $format = $this->getRequestFormat($request, $this->documentationFormats); + + if ('html' === $format || OpenApiNormalizer::FORMAT === $format || OpenApiNormalizer::JSON_FORMAT === $format || OpenApiNormalizer::YAML_FORMAT === $format) { + return $this->getOpenApiDocumentation($context, $format, $request); + } + + return $this->getHydraDocumentation($context, $request); + } + + /** + * @param array $context + */ + private function getOpenApiDocumentation(array $context, string $format, Request $request): Response + { + $context['request'] = $request; + $operation = new Get( + class: OpenApi::class, + read: true, + serialize: true, + provider: fn () => $this->openApiFactory->__invoke($context), + normalizationContext: [ + ApiGatewayNormalizer::API_GATEWAY => $context['api_gateway'] ?? null, + LegacyOpenApiNormalizer::SPEC_VERSION => $context['spec_version'] ?? null, + ], + outputFormats: $this->documentationFormats + ); + + if ('html' === $format && $this->swaggerUiEnabled) { + $operation = $operation->withProcessor('api_platform.swagger_ui.processor')->withWrite(true); + } + + return $this->processor->process($this->provider->provide($operation, [], $context), $operation, [], $context); + } + + /** + * TODO: the logic behind the Hydra Documentation is done in a ApiPlatform\Hydra\Serializer\DocumentationNormalizer. + * We should transform this to a provider, it'd improve performances also by a bit. + * + * @param array $context + */ + private function getHydraDocumentation(array $context, Request $request): Response + { + $context['request'] = $request; + $operation = new Get( + class: Documentation::class, + read: true, + serialize: true, + provider: fn () => new Documentation($this->resourceNameCollectionFactory->create(), $this->title, $this->description, $this->version) + ); + + return $this->processor->process($this->provider->provide($operation, [], $context), $operation, [], $context); + } +} diff --git a/src/Laravel/Controller/EntrypointController.php b/src/Laravel/Controller/EntrypointController.php new file mode 100644 index 00000000000..d013352c0b6 --- /dev/null +++ b/src/Laravel/Controller/EntrypointController.php @@ -0,0 +1,74 @@ + + * + * 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\Laravel\Controller; + +use ApiPlatform\Documentation\Entrypoint; +use ApiPlatform\Metadata\Get; +use ApiPlatform\Metadata\Resource\Factory\ResourceNameCollectionFactoryInterface; +use ApiPlatform\Metadata\Resource\ResourceNameCollection; +use ApiPlatform\OpenApi\Serializer\LegacyOpenApiNormalizer; +use ApiPlatform\State\ProcessorInterface; +use ApiPlatform\State\ProviderInterface; +use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\HttpFoundation\Response; + +/** + * Generates the API entrypoint. + * + * @author Kévin Dunglas + */ +final class EntrypointController +{ + private static ResourceNameCollection $resourceNameCollection; + + /** + * @param array $documentationFormats + * @param ProviderInterface $provider + * @param ProcessorInterface $processor + */ + public function __construct( + private readonly ResourceNameCollectionFactoryInterface $resourceNameCollectionFactory, + private readonly ProviderInterface $provider, + private readonly ProcessorInterface $processor, + private readonly array $documentationFormats = [], + ) { + } + + public function __invoke(Request $request): Response + { + self::$resourceNameCollection = $this->resourceNameCollectionFactory->create(); + $context = [ + 'request' => $request, + 'spec_version' => (string) $request->query->get(LegacyOpenApiNormalizer::SPEC_VERSION), + ]; + $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(self::$resourceNameCollection); + } +} diff --git a/src/Laravel/State/SwaggerUiProvider.php b/src/Laravel/State/SwaggerUiProvider.php index 25338b58ba9..e951fe2e00c 100644 --- a/src/Laravel/State/SwaggerUiProvider.php +++ b/src/Laravel/State/SwaggerUiProvider.php @@ -37,6 +37,7 @@ final class SwaggerUiProvider implements ProviderInterface public function __construct( private readonly ProviderInterface $decorated, private readonly OpenApiFactoryInterface $openApiFactory, + private readonly bool $swaggerUiEnabled = true, ) { } @@ -51,6 +52,7 @@ public function provide(Operation $operation, array $uriVariables = [], array $c !($operation instanceof HttpOperation) || !($request = $context['request'] ?? null) || 'html' !== $request->getRequestFormat() + || !$this->swaggerUiEnabled ) { return $this->decorated->provide($operation, $uriVariables, $context); } diff --git a/src/Laravel/Tests/AuthTest.php b/src/Laravel/Tests/AuthTest.php index c8507f25e00..1a9ec993b98 100644 --- a/src/Laravel/Tests/AuthTest.php +++ b/src/Laravel/Tests/AuthTest.php @@ -44,7 +44,7 @@ public function testAuthenticatedPolicy(): void { $response = $this->post('/tokens/create'); $token = $response->json()['token']; - $response = $this->post('/api/vaults', [], ['content-type' => ['application/ld+json'], 'authorization' => 'Bearer '.$token]); + $response = $this->post('/api/vaults', [], ['accept' => ['application/ld+json'], 'content-type' => ['application/ld+json'], 'authorization' => 'Bearer '.$token]); $response->assertStatus(403); } } diff --git a/src/Laravel/Tests/DocsTest.php b/src/Laravel/Tests/DocsTest.php new file mode 100644 index 00000000000..9d155f041d3 --- /dev/null +++ b/src/Laravel/Tests/DocsTest.php @@ -0,0 +1,52 @@ + + * + * 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\Laravel\Tests; + +use ApiPlatform\Laravel\Test\ApiTestAssertionsTrait; +use Orchestra\Testbench\Concerns\WithWorkbench; +use Orchestra\Testbench\TestCase; + +class DocsTest extends TestCase +{ + use ApiTestAssertionsTrait; + use WithWorkbench; + + public function testOpenApi(): void + { + $res = $this->get('/api/docs.jsonopenapi'); + $this->assertArrayHasKey('openapi', $res->json()); + $this->assertSame('application/vnd.openapi+json; charset=utf-8', $res->headers->get('content-type')); + } + + public function testOpenApiAccept(): void + { + $res = $this->get('/api/docs', headers: ['accept' => 'application/vnd.openapi+json']); + $this->assertArrayHasKey('openapi', $res->json()); + $this->assertSame('application/vnd.openapi+json; charset=utf-8', $res->headers->get('content-type')); + } + + public function testJsonLd(): void + { + $res = $this->get('/api/docs.jsonld'); + $this->assertArrayHasKey('@context', $res->json()); + $this->assertSame('application/ld+json; charset=utf-8', $res->headers->get('content-type')); + } + + public function testJsonLdAccept(): void + { + $res = $this->get('/api/docs', headers: ['accept' => 'application/ld+json']); + $this->assertArrayHasKey('@context', $res->json()); + $this->assertSame('application/ld+json; charset=utf-8', $res->headers->get('content-type')); + } +} diff --git a/src/Laravel/Tests/JsonApiTest.php b/src/Laravel/Tests/JsonApiTest.php index 50a94c107ef..610ad42d702 100644 --- a/src/Laravel/Tests/JsonApiTest.php +++ b/src/Laravel/Tests/JsonApiTest.php @@ -184,7 +184,7 @@ public function testDeleteBook(): void { $book = Book::first(); $iri = $this->getIriFromResource($book); - $response = $this->delete($iri, ['accept' => 'application/vnd.api+json']); + $response = $this->delete($iri, headers: ['accept' => 'application/vnd.api+json']); $response->assertStatus(204); $this->assertNull(Book::find($book->id)); } diff --git a/src/Laravel/config/api-platform.php b/src/Laravel/config/api-platform.php index 2f6bc0b709f..88497a5f6c2 100644 --- a/src/Laravel/config/api-platform.php +++ b/src/Laravel/config/api-platform.php @@ -69,5 +69,9 @@ 'exception_to_status' => [ AuthenticationException::class => 401, AuthorizationException::class => 403 + ], + + 'swagger_ui' => [ + 'enabled' => true ] ]; diff --git a/src/Metadata/Resource/Factory/FormatsResourceMetadataCollectionFactory.php b/src/Metadata/Resource/Factory/FormatsResourceMetadataCollectionFactory.php index 4070884317d..b090fddfc12 100644 --- a/src/Metadata/Resource/Factory/FormatsResourceMetadataCollectionFactory.php +++ b/src/Metadata/Resource/Factory/FormatsResourceMetadataCollectionFactory.php @@ -33,8 +33,12 @@ */ final class FormatsResourceMetadataCollectionFactory implements ResourceMetadataCollectionFactoryInterface { - public function __construct(private readonly ResourceMetadataCollectionFactoryInterface $decorated, private readonly array $formats, private readonly array $patchFormats, private readonly ?array $errorFormats = null) - { + public function __construct( + private readonly ResourceMetadataCollectionFactoryInterface $decorated, + private readonly array $formats, + private readonly array $patchFormats, + private readonly ?array $errorFormats = null, + ) { } /** diff --git a/src/State/Provider/ContentNegotiationProvider.php b/src/State/Provider/ContentNegotiationProvider.php index 0ffd42607b6..f01b3e268d0 100644 --- a/src/State/Provider/ContentNegotiationProvider.php +++ b/src/State/Provider/ContentNegotiationProvider.php @@ -46,12 +46,7 @@ public function provide(Operation $operation, array $uriVariables = [], array $c $formats = $operation->getOutputFormats() ?? ($isErrorOperation ? $this->errorFormats : $this->formats); $this->addRequestFormats($request, $formats); $request->attributes->set('input_format', $this->getInputFormat($operation, $request)); - - if (!$isErrorOperation) { - $request->setRequestFormat($this->getRequestFormat($request, $formats)); - } else { - $request->setRequestFormat($this->getRequestFormat($request, $formats, false)); - } + $request->setRequestFormat($this->getRequestFormat($request, $formats, !$isErrorOperation)); return $this->decorated?->provide($operation, $uriVariables, $context); } diff --git a/src/Symfony/Bundle/EventListener/SwaggerUiListener.php b/src/Symfony/Bundle/EventListener/SwaggerUiListener.php deleted file mode 100644 index 094d2ff6f23..00000000000 --- a/src/Symfony/Bundle/EventListener/SwaggerUiListener.php +++ /dev/null @@ -1,39 +0,0 @@ - - * - * 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\Bundle\EventListener; - -use Symfony\Component\HttpKernel\Event\RequestEvent; - -final class SwaggerUiListener -{ - /** - * Sets SwaggerUiAction as controller if the requested format is HTML. - */ - public function onKernelRequest(RequestEvent $event): void - { - $request = $event->getRequest(); - if ( - 'html' !== $request->getRequestFormat('') - || !($request->attributes->has('_api_resource_class') || $request->attributes->getBoolean('_api_respond', false)) - ) { - return; - } - - if (($operation = $request->attributes->get('_api_operation')) && 'api_platform.symfony.main_controller' === $operation->getController()) { - return; - } - - $request->attributes->set('_controller', 'api_platform.swagger_ui.action'); - } -} diff --git a/tests/Symfony/Bundle/EventListener/SwaggerUiListenerTest.php b/tests/Symfony/Bundle/EventListener/SwaggerUiListenerTest.php deleted file mode 100644 index 53abf9bfe94..00000000000 --- a/tests/Symfony/Bundle/EventListener/SwaggerUiListenerTest.php +++ /dev/null @@ -1,59 +0,0 @@ - - * - * 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\Symfony\Bundle\EventListener; - -use ApiPlatform\Symfony\Bundle\EventListener\SwaggerUiListener; -use PHPUnit\Framework\TestCase; -use Prophecy\PhpUnit\ProphecyTrait; -use Symfony\Component\HttpFoundation\Request; -use Symfony\Component\HttpKernel\Event\RequestEvent; - -/** - * @author Kévin Dunglas - */ -class SwaggerUiListenerTest extends TestCase -{ - use ProphecyTrait; - - #[\PHPUnit\Framework\Attributes\DataProvider('getParameters')] - public function testOnKernelRequest(Request $request, ?string $controller = null): void - { - $eventProphecy = $this->prophesize(RequestEvent::class); - $eventProphecy->getRequest()->willReturn($request)->shouldBeCalled(); - - $listener = new SwaggerUiListener(); - $listener->onKernelRequest($eventProphecy->reveal()); - - $this->assertSame($controller, $request->attributes->get('_controller')); - } - - public static function getParameters(): array - { - $respondRequest = new Request([], [], ['_api_respond' => true]); - $respondRequest->setRequestFormat('html'); - - $resourceClassRequest = new Request([], [], ['_api_resource_class' => 'Foo']); - $resourceClassRequest->setRequestFormat('html'); - - $jsonRequest = new Request([], [], ['_api_resource_class' => 'Foo']); - $jsonRequest->setRequestFormat('json'); - - return [ - [$respondRequest, 'api_platform.swagger_ui.action'], - [$resourceClassRequest, 'api_platform.swagger_ui.action'], - [new Request(), null], - [$jsonRequest, null], - ]; - } -}