From 3eaa68d991d25d9e656c3f8ea1f11c7e10d0be66 Mon Sep 17 00:00:00 2001 From: Bram Leeda Date: Fri, 2 Oct 2026 22:33:40 +0200 Subject: [PATCH] fix(symfony): hides the "Other API docs" label when there is only a single doc --- .../Resources/views/SwaggerUi/index.html.twig | 23 ++++--- tests/Functional/RedocOtherApiDocLinkTest.php | 69 +++++++++++++++++++ .../SwaggerUiOtherApiDocLinkTest.php | 69 +++++++++++++++++++ 3 files changed, 153 insertions(+), 8 deletions(-) create mode 100644 tests/Functional/RedocOtherApiDocLinkTest.php create mode 100644 tests/Functional/SwaggerUiOtherApiDocLinkTest.php diff --git a/src/Symfony/Bundle/Resources/views/SwaggerUi/index.html.twig b/src/Symfony/Bundle/Resources/views/SwaggerUi/index.html.twig index 7c026ff061..294b30a5dc 100644 --- a/src/Symfony/Bundle/Resources/views/SwaggerUi/index.html.twig +++ b/src/Symfony/Bundle/Resources/views/SwaggerUi/index.html.twig @@ -10,7 +10,7 @@ {% if title %}{{ title }} - {% endif %}API Platform {% endblock %} - {% set active_ui = app.request.query.get('ui', 'swagger_ui') %} + {% set active_ui = app.request.query.get('ui', reDocEnabled and not swaggerUiEnabled ? 're_doc' : 'swagger_ui') %} {% set is_scalar = (scalarEnabled and not swaggerUiEnabled and not reDocEnabled) or (scalarEnabled and 'scalar' == active_ui) %} {%- set csp_nonce_attr = cspNonce is defined and cspNonce is not null ? ' nonce="' ~ (cspNonce|e('html_attr')) ~ '"' : '' -%} @@ -90,12 +90,19 @@ {% for format in formats|keys %} {{ format }} {% endfor %} -
- Other API docs: - {% if swaggerUiEnabled and active_ui != 'swagger_ui' %}Swagger UI{% endif %} - {% if reDocEnabled and active_ui != 're_doc' %}ReDoc{% endif %} - {% if scalarEnabled and active_ui != 'scalar' %}Scalar{% endif %} - {% if graphiQlEnabled %}GraphiQL{% endif %} + + {% set swaggerUiOption = swaggerUiEnabled and active_ui != 'swagger_ui' %} + {% set reDocOption = reDocEnabled and active_ui != 're_doc' %} + {% set scalarOption = scalarEnabled and active_ui != 'scalar' %} + + {% if swaggerUiOption or reDocOption or scalarOption or graphiQlEnabled %} +
+ Other API docs: + {% if swaggerUiOption %}Swagger UI{% endif %} + {% if reDocOption %}ReDoc{% endif %} + {% if scalarOption %}Scalar{% endif %} + {% if graphiQlEnabled %}GraphiQL{% endif %} + {% endif %} @@ -105,7 +112,7 @@ {% if is_scalar %} - {% elseif (reDocEnabled and not swaggerUiEnabled) or (reDocEnabled and 're_doc' == active_ui) %} + {% elseif (reDocEnabled and 're_doc' == active_ui) %} {% else %} diff --git a/tests/Functional/RedocOtherApiDocLinkTest.php b/tests/Functional/RedocOtherApiDocLinkTest.php new file mode 100644 index 0000000000..3129427144 --- /dev/null +++ b/tests/Functional/RedocOtherApiDocLinkTest.php @@ -0,0 +1,69 @@ + + * + * 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\Functional; + +use ApiPlatform\Test\ApiTestCase; +use Symfony\Component\Config\Loader\LoaderInterface; +use Symfony\Component\DependencyInjection\ContainerBuilder; + +class RedocOtherApiDocLinkAppKernel extends \AppKernel +{ + public function getCacheDir(): string + { + return parent::getCacheDir().'/redoc_otherapidoc_link'; + } + + public function getLogDir(): string + { + return parent::getLogDir().'/redoc_otherapidoc_link'; + } + + protected function configureContainer(ContainerBuilder $c, LoaderInterface $loader): void + { + parent::configureContainer($c, $loader); + + $loader->load(static function (ContainerBuilder $container): void { + $container->loadFromExtension('api_platform', [ + 'enable_swagger_ui' => false, + 'enable_re_doc' => true, + 'enable_scalar' => false, + 'graphql' => ['enabled' => false], + ]); + + // the fixtures decorate a GraphQL service that is not registered once GraphQL is off + if ($container->hasDefinition('app.graphql.type_converter')) { + $container->removeDefinition('app.graphql.type_converter'); + } + }); + } +} + +final class RedocOtherApiDocLinkTest extends ApiTestCase +{ + protected static ?bool $alwaysBootKernel = true; + + protected static function getKernelClass(): string + { + return RedocOtherApiDocLinkAppKernel::class; + } + + public function testOtherApiDocsLinkAreHiddenWhenAllOptionsAreDisabled(): void + { + $client = self::createClient(); + $client->request('GET', '/docs', ['headers' => ['Accept' => 'text/html']]); + + $this->assertResponseIsSuccessful(); + $this->assertStringNotContainsString('Other API docs:', $client->getResponse()->getContent()); + } +} diff --git a/tests/Functional/SwaggerUiOtherApiDocLinkTest.php b/tests/Functional/SwaggerUiOtherApiDocLinkTest.php new file mode 100644 index 0000000000..27b4d4c53c --- /dev/null +++ b/tests/Functional/SwaggerUiOtherApiDocLinkTest.php @@ -0,0 +1,69 @@ + + * + * 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\Functional; + +use ApiPlatform\Test\ApiTestCase; +use Symfony\Component\Config\Loader\LoaderInterface; +use Symfony\Component\DependencyInjection\ContainerBuilder; + +class SwaggerUiOtherApiDocLinkAppKernel extends \AppKernel +{ + public function getCacheDir(): string + { + return parent::getCacheDir().'/swagger_otherapidoc_link'; + } + + public function getLogDir(): string + { + return parent::getLogDir().'/swagger_otherapidoc_link'; + } + + protected function configureContainer(ContainerBuilder $c, LoaderInterface $loader): void + { + parent::configureContainer($c, $loader); + + $loader->load(static function (ContainerBuilder $container): void { + $container->loadFromExtension('api_platform', [ + 'enable_swagger_ui' => true, + 'enable_re_doc' => false, + 'enable_scalar' => false, + 'graphql' => ['enabled' => false], + ]); + + // the fixtures decorate a GraphQL service that is not registered once GraphQL is off + if ($container->hasDefinition('app.graphql.type_converter')) { + $container->removeDefinition('app.graphql.type_converter'); + } + }); + } +} + +final class SwaggerUiOtherApiDocLinkTest extends ApiTestCase +{ + protected static ?bool $alwaysBootKernel = true; + + protected static function getKernelClass(): string + { + return SwaggerUiOtherApiDocLinkAppKernel::class; + } + + public function testOtherApiDocsLinkAreHiddenWhenAllOptionsAreDisabled(): void + { + $client = self::createClient(); + $client->request('GET', '/docs', ['headers' => ['Accept' => 'text/html']]); + + $this->assertResponseIsSuccessful(); + $this->assertStringNotContainsString('Other API docs:', $client->getResponse()->getContent()); + } +}