Skip to content

Commit 8c513c0

Browse files
committed
fix(openapi): enable ReDoc when Swagger UI is disabled
1 parent 2a34498 commit 8c513c0

File tree

7 files changed

+136
-11
lines changed

7 files changed

+136
-11
lines changed

src/Symfony/Action/DocumentationAction.php

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,7 @@ public function __construct(
5050
?Negotiator $negotiator = null,
5151
private readonly array $documentationFormats = [OpenApiNormalizer::JSON_FORMAT => ['application/vnd.openapi+json'], OpenApiNormalizer::FORMAT => ['application/json']],
5252
private readonly bool $swaggerUiEnabled = true,
53+
private readonly bool $reDocEnabled = true,
5354
private readonly bool $docsEnabled = true,
5455
) {
5556
$this->negotiator = $negotiator ?? new Negotiator();
@@ -90,8 +91,8 @@ public function __invoke(?Request $request = null)
9091
*/
9192
private function getOpenApiDocumentation(array $context, string $format, Request $request): OpenApi|Response
9293
{
93-
if ('html' === $format && !$this->swaggerUiEnabled) {
94-
throw new NotFoundHttpException('Swagger UI is disabled.');
94+
if ('html' === $format && !$this->swaggerUiEnabled && !$this->reDocEnabled) {
95+
throw new NotFoundHttpException('Swagger UI and ReDoc are disabled.');
9596
}
9697

9798
if ($this->provider && $this->processor) {
@@ -104,7 +105,7 @@ class: OpenApi::class,
104105
outputFormats: $this->documentationFormats
105106
);
106107

107-
if ('html' === $format && $this->swaggerUiEnabled) {
108+
if ('html' === $format && ($this->swaggerUiEnabled || $this->reDocEnabled)) {
108109
$operation = $operation->withProcessor('api_platform.swagger_ui.processor')->withWrite(true);
109110
}
110111

src/Symfony/Bundle/DependencyInjection/ApiPlatformExtension.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -604,7 +604,7 @@ private function registerSwaggerConfiguration(ContainerBuilder $container, array
604604
$loader->load('openapi/yaml.php');
605605
}
606606

607-
if ($config['enable_swagger_ui']) {
607+
if ($config['enable_swagger_ui'] || $config['enable_re_doc']) {
608608
$loader->load('swagger_ui.php');
609609

610610
if ($config['use_symfony_listeners']) {

src/Symfony/Bundle/Resources/config/symfony/controller.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,7 @@
4848
service('api_platform.negotiator')->nullOnInvalid(),
4949
'%api_platform.docs_formats%',
5050
'%api_platform.enable_swagger_ui%',
51+
'%api_platform.enable_re_doc%',
5152
'%api_platform.enable_docs%',
5253
]);
5354
};

src/Symfony/Bundle/Resources/config/symfony/events.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -180,6 +180,7 @@
180180
service('api_platform.negotiator')->nullOnInvalid(),
181181
'%api_platform.docs_formats%',
182182
'%api_platform.enable_swagger_ui%',
183+
'%api_platform.enable_re_doc%',
183184
'%api_platform.enable_docs%',
184185
]);
185186

src/Symfony/Tests/Action/DocumentationActionTest.php

Lines changed: 61 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -36,10 +36,10 @@ class DocumentationActionTest extends TestCase
3636
{
3737
use ProphecyTrait;
3838

39-
public function testHtmlFormatWhenSwaggerUiDisabledThrows404(): void
39+
public function testHtmlFormatWhenSwaggerUiAndReDocDisabledThrows404(): void
4040
{
4141
$this->expectException(NotFoundHttpException::class);
42-
$this->expectExceptionMessage('Swagger UI is disabled.');
42+
$this->expectExceptionMessage('Swagger UI and ReDoc are disabled.');
4343

4444
$request = new Request();
4545
$request->attributes->set('_format', 'html');
@@ -55,11 +55,70 @@ public function testHtmlFormatWhenSwaggerUiDisabledThrows404(): void
5555
'html' => ['text/html'],
5656
],
5757
swaggerUiEnabled: false,
58+
reDocEnabled: false,
5859
);
5960

6061
$documentation($request);
6162
}
6263

64+
public function testHtmlFormatWhenReDocEnabledAndSwaggerUiDisabled(): void
65+
{
66+
$request = new Request();
67+
$request->attributes->set('_format', 'html');
68+
69+
$openApiFactory = $this->createMock(OpenApiFactoryInterface::class);
70+
$resourceNameCollectionFactory = $this->createMock(ResourceNameCollectionFactoryInterface::class);
71+
$provider = $this->createMock(ProviderInterface::class);
72+
$provider->expects($this->once())->method('provide')->willReturn(new OpenApi(new Info('title', '1.0.0'), [], new Paths()));
73+
$processor = $this->createMock(ProcessorInterface::class);
74+
$processor->expects($this->once())->method('process')->willReturnArgument(0);
75+
76+
$documentation = new DocumentationAction(
77+
$resourceNameCollectionFactory,
78+
openApiFactory: $openApiFactory,
79+
provider: $provider,
80+
processor: $processor,
81+
documentationFormats: [
82+
'json' => ['application/json'],
83+
'html' => ['text/html'],
84+
],
85+
swaggerUiEnabled: false,
86+
reDocEnabled: true,
87+
);
88+
89+
$result = $documentation($request);
90+
$this->assertInstanceOf(OpenApi::class, $result);
91+
}
92+
93+
public function testHtmlFormatWhenSwaggerUiEnabledAndReDocDisabled(): void
94+
{
95+
$request = new Request();
96+
$request->attributes->set('_format', 'html');
97+
98+
$openApiFactory = $this->createMock(OpenApiFactoryInterface::class);
99+
$resourceNameCollectionFactory = $this->createMock(ResourceNameCollectionFactoryInterface::class);
100+
$provider = $this->createMock(ProviderInterface::class);
101+
$provider->expects($this->once())->method('provide')->willReturn(new OpenApi(new Info('title', '1.0.0'), [], new Paths()));
102+
$processor = $this->createMock(ProcessorInterface::class);
103+
$processor->expects($this->once())->method('process')->willReturnArgument(0);
104+
105+
$documentation = new DocumentationAction(
106+
$resourceNameCollectionFactory,
107+
openApiFactory: $openApiFactory,
108+
provider: $provider,
109+
processor: $processor,
110+
documentationFormats: [
111+
'json' => ['application/json'],
112+
'html' => ['text/html'],
113+
],
114+
swaggerUiEnabled: true,
115+
reDocEnabled: false,
116+
);
117+
118+
$result = $documentation($request);
119+
$this->assertInstanceOf(OpenApi::class, $result);
120+
}
121+
63122
public function testJsonFormatWhenSwaggerUiDisabledIsAccessible(): void
64123
{
65124
$request = new Request();

src/Symfony/Tests/Bundle/DependencyInjection/ApiPlatformExtensionTest.php

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -268,6 +268,7 @@ public function testSwaggerUiDisabledConfiguration(): void
268268
$config = self::DEFAULT_CONFIG;
269269
$config['api_platform']['enable_swagger'] = true;
270270
$config['api_platform']['enable_swagger_ui'] = false;
271+
$config['api_platform']['enable_re_doc'] = false;
271272
$config['api_platform']['use_symfony_listeners'] = true;
272273

273274
(new ApiPlatformExtension())->load($config, $this->container);
@@ -300,6 +301,26 @@ public function testSwaggerUiEnabledConfiguration(): void
300301
$this->assertContainerHas($services);
301302
}
302303

304+
public function testReDocEnabledWithSwaggerUiDisabledConfiguration(): void
305+
{
306+
$config = self::DEFAULT_CONFIG;
307+
$config['api_platform']['enable_swagger'] = true;
308+
$config['api_platform']['enable_swagger_ui'] = false;
309+
$config['api_platform']['enable_re_doc'] = true;
310+
$config['api_platform']['use_symfony_listeners'] = true;
311+
312+
(new ApiPlatformExtension())->load($config, $this->container);
313+
314+
$services = [
315+
'api_platform.swagger_ui.processor',
316+
'api_platform.swagger_ui.context',
317+
'api_platform.swagger_ui.provider',
318+
'api_platform.swagger_ui.documentation.provider',
319+
];
320+
321+
$this->assertContainerHas($services);
322+
}
323+
303324
public function testEventListenersConfiguration(): void
304325
{
305326
$config = self::DEFAULT_CONFIG;

tests/Functional/DocumentationActionTest.php

Lines changed: 47 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -23,17 +23,18 @@
2323
class DocumentationActionAppKernel extends \AppKernel
2424
{
2525
public static bool $swaggerUiEnabled = true;
26+
public static bool $reDocEnabled = true;
2627

2728
public function getCacheDir(): string
2829
{
29-
$suffix = self::$swaggerUiEnabled ? 'ui_enabled' : 'ui_disabled';
30+
$suffix = (self::$swaggerUiEnabled ? 'ui_' : 'no_ui_').(self::$reDocEnabled ? 'redoc' : 'no_redoc');
3031

3132
return parent::getCacheDir().'/'.$suffix;
3233
}
3334

3435
public function getLogDir(): string
3536
{
36-
$suffix = self::$swaggerUiEnabled ? 'ui_enabled' : 'ui_disabled';
37+
$suffix = (self::$swaggerUiEnabled ? 'ui_' : 'no_ui_').(self::$reDocEnabled ? 'redoc' : 'no_redoc');
3738

3839
return parent::getLogDir().'/'.$suffix;
3940
}
@@ -45,6 +46,7 @@ protected function configureContainer(ContainerBuilder $c, LoaderInterface $load
4546
$loader->load(static function (ContainerBuilder $container) {
4647
$container->loadFromExtension('api_platform', [
4748
'enable_swagger_ui' => DocumentationActionAppKernel::$swaggerUiEnabled,
49+
'enable_re_doc' => DocumentationActionAppKernel::$reDocEnabled,
4850
]);
4951
});
5052
}
@@ -59,57 +61,97 @@ protected static function getKernelClass(): string
5961
return DocumentationActionAppKernel::class;
6062
}
6163

62-
public function testHtmlDocumentationIsNotAccessibleWhenSwaggerUiIsDisabled(): void
64+
public function testHtmlDocumentationIsNotAccessibleWhenSwaggerUiAndReDocAreDisabled(): void
6365
{
6466
DocumentationActionAppKernel::$swaggerUiEnabled = false;
67+
DocumentationActionAppKernel::$reDocEnabled = false;
6568

6669
$client = self::createClient();
6770

6871
$container = static::getContainer();
6972
$this->assertFalse($container->getParameter('api_platform.enable_swagger_ui'));
73+
$this->assertFalse($container->getParameter('api_platform.enable_re_doc'));
7074

7175
$client->request('GET', '/docs', ['headers' => ['Accept' => 'text/html']]);
7276
$this->assertResponseStatusCodeSame(404);
73-
$this->assertStringContainsString('Swagger UI is disabled.', $client->getResponse()->getContent(false));
77+
$this->assertStringContainsString('Swagger UI and ReDoc are disabled.', $client->getResponse()->getContent(false));
7478
}
7579

7680
public function testJsonDocumentationIsAccessibleWhenSwaggerUiIsDisabled(): void
7781
{
7882
DocumentationActionAppKernel::$swaggerUiEnabled = false;
83+
DocumentationActionAppKernel::$reDocEnabled = false;
7984

8085
$client = self::createClient();
8186

8287
$container = static::getContainer();
8388
$this->assertFalse($container->getParameter('api_platform.enable_swagger_ui'));
89+
$this->assertFalse($container->getParameter('api_platform.enable_re_doc'));
8490

8591
$client->request('GET', '/docs.jsonopenapi', ['headers' => ['Accept' => 'application/vnd.openapi+json']]);
8692
$this->assertResponseIsSuccessful();
8793
$this->assertJsonContains(['openapi' => '3.1.0']);
8894
$this->assertJsonContains(['info' => ['title' => 'My Dummy API']]);
8995
}
9096

97+
public function testHtmlDocumentationIsAccessibleWhenReDocEnabledAndSwaggerUiDisabled(): void
98+
{
99+
DocumentationActionAppKernel::$swaggerUiEnabled = false;
100+
DocumentationActionAppKernel::$reDocEnabled = true;
101+
102+
$client = self::createClient();
103+
104+
$container = static::getContainer();
105+
$this->assertFalse($container->getParameter('api_platform.enable_swagger_ui'));
106+
$this->assertTrue($container->getParameter('api_platform.enable_re_doc'));
107+
108+
$client->request('GET', '/docs', ['headers' => ['Accept' => 'text/html']]);
109+
$this->assertResponseIsSuccessful();
110+
$this->assertStringNotContainsString('Swagger UI and ReDoc are disabled.', $client->getResponse()->getContent(false));
111+
}
112+
113+
public function testHtmlDocumentationIsAccessibleWhenSwaggerUiEnabledAndReDocDisabled(): void
114+
{
115+
DocumentationActionAppKernel::$swaggerUiEnabled = true;
116+
DocumentationActionAppKernel::$reDocEnabled = false;
117+
118+
$client = self::createClient();
119+
120+
$container = static::getContainer();
121+
$this->assertTrue($container->getParameter('api_platform.enable_swagger_ui'));
122+
$this->assertFalse($container->getParameter('api_platform.enable_re_doc'));
123+
124+
$client->request('GET', '/docs', ['headers' => ['Accept' => 'text/html']]);
125+
$this->assertResponseIsSuccessful();
126+
$this->assertStringNotContainsString('Swagger UI and ReDoc are disabled.', $client->getResponse()->getContent(false));
127+
}
128+
91129
public function testHtmlDocumentationIsAccessibleWhenSwaggerUiIsEnabled(): void
92130
{
93131
DocumentationActionAppKernel::$swaggerUiEnabled = true;
132+
DocumentationActionAppKernel::$reDocEnabled = true;
94133

95134
$client = self::createClient();
96135

97136
$container = static::getContainer();
98137
$this->assertTrue($container->getParameter('api_platform.enable_swagger_ui'));
138+
$this->assertTrue($container->getParameter('api_platform.enable_re_doc'));
99139

100140
$client->request('GET', '/docs', ['headers' => ['Accept' => 'text/html']]);
101141
$this->assertResponseIsSuccessful();
102-
$this->assertStringNotContainsString('Swagger UI is disabled.', $client->getResponse()->getContent(false));
142+
$this->assertStringNotContainsString('Swagger UI and ReDoc are disabled.', $client->getResponse()->getContent(false));
103143
}
104144

105145
public function testJsonDocumentationIsAccessibleWhenSwaggerUiIsEnabled(): void
106146
{
107147
DocumentationActionAppKernel::$swaggerUiEnabled = true;
148+
DocumentationActionAppKernel::$reDocEnabled = true;
108149

109150
$client = self::createClient();
110151

111152
$container = static::getContainer();
112153
$this->assertTrue($container->getParameter('api_platform.enable_swagger_ui'));
154+
$this->assertTrue($container->getParameter('api_platform.enable_re_doc'));
113155

114156
$client->request('GET', '/docs.jsonopenapi', ['headers' => ['Accept' => 'application/vnd.openapi+json']]);
115157
$this->assertResponseIsSuccessful();

0 commit comments

Comments
 (0)