From 4ebee933c422b77647afaadb56179d04e8de85f4 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Zdene=CC=8Ck=20Drahos=CC=8C?=
Date: Sun, 27 Jun 2021 08:41:33 +0200
Subject: [PATCH 1/9] Extract rendering docs from command and controller
---
Command/DumpCommand.php | 32 ++++--------
Controller/SwaggerUiController.php | 52 ++++++++------------
Render/Html/HtmlOpenApiRenderer.php | 56 +++++++++++++++++++++
Render/Json/JsonOpenApiRenderer.php | 34 +++++++++++++
Render/OpenApiRenderer.php | 21 ++++++++
Render/RenderOpenApi.php | 53 ++++++++++++++++++++
Resources/config/services.xml | 17 +++++--
Tests/Command/DumpCommandTest.php | 31 +++++++++---
Tests/Render/RenderOpenApiTest.php | 75 +++++++++++++++++++++++++++++
9 files changed, 305 insertions(+), 66 deletions(-)
create mode 100644 Render/Html/HtmlOpenApiRenderer.php
create mode 100644 Render/Json/JsonOpenApiRenderer.php
create mode 100644 Render/OpenApiRenderer.php
create mode 100644 Render/RenderOpenApi.php
create mode 100644 Tests/Render/RenderOpenApiTest.php
diff --git a/Command/DumpCommand.php b/Command/DumpCommand.php
index f1b6271..b2438da 100644
--- a/Command/DumpCommand.php
+++ b/Command/DumpCommand.php
@@ -11,9 +11,8 @@
namespace Nelmio\ApiDocBundle\Command;
-use Psr\Container\ContainerInterface;
+use Nelmio\ApiDocBundle\Render\RenderOpenApi;
use Symfony\Component\Console\Command\Command;
-use Symfony\Component\Console\Exception\InvalidArgumentException;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Output\OutputInterface;
@@ -21,16 +20,13 @@ use Symfony\Component\Console\Output\OutputInterface;
class DumpCommand extends Command
{
/**
- * @var ContainerInterface
+ * @var RenderOpenApi
*/
- private $generatorLocator;
+ private $renderOpenApi;
- /**
- * DumpCommand constructor.
- */
- public function __construct(ContainerInterface $generatorLocator)
+ public function __construct(RenderOpenApi $renderOpenApi)
{
- $this->generatorLocator = $generatorLocator;
+ $this->renderOpenApi = $renderOpenApi;
parent::__construct();
}
@@ -48,25 +44,17 @@ class DumpCommand extends Command
}
/**
- * @throws InvalidArgumentException If the area to dump is not valid
- *
* @return int|void
*/
protected function execute(InputInterface $input, OutputInterface $output)
{
$area = $input->getOption('area');
- if (!$this->generatorLocator->has($area)) {
- throw new InvalidArgumentException(sprintf('Area "%s" is not supported.', $area));
- }
-
- $spec = $this->generatorLocator->get($area)->generate();
-
- if ($input->hasParameterOption(['--no-pretty'])) {
- $output->writeln(json_encode($spec));
- } else {
- $output->writeln(json_encode($spec, JSON_PRETTY_PRINT));
- }
+ $options = [
+ 'no-pretty' => $input->hasParameterOption(['--no-pretty']),
+ ];
+ $docs = $this->renderOpenApi->render(RenderOpenApi::JSON, $area, $options);
+ $output->writeln($docs, OutputInterface::OUTPUT_RAW);
return 0;
}
diff --git a/Controller/SwaggerUiController.php b/Controller/SwaggerUiController.php
index 2b3d962..4edad7f 100644
--- a/Controller/SwaggerUiController.php
+++ b/Controller/SwaggerUiController.php
@@ -11,33 +11,37 @@
namespace Nelmio\ApiDocBundle\Controller;
-use OpenApi\Annotations\OpenApi;
-use OpenApi\Annotations\Server;
-use Psr\Container\ContainerInterface;
+use InvalidArgumentException;
+use Nelmio\ApiDocBundle\Render\RenderOpenApi;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpKernel\Exception\BadRequestHttpException;
-use Twig\Environment;
final class SwaggerUiController
{
- private $generatorLocator;
+ /**
+ * @var RenderOpenApi
+ */
+ private $renderOpenApi;
- private $twig;
-
- public function __construct(ContainerInterface $generatorLocator, $twig)
+ public function __construct(RenderOpenApi $renderOpenApi)
{
- if (!$twig instanceof \Twig_Environment && !$twig instanceof Environment) {
- throw new \InvalidArgumentException(sprintf('Providing an instance of "%s" as twig is not supported.', get_class($twig)));
- }
-
- $this->generatorLocator = $generatorLocator;
- $this->twig = $twig;
+ $this->renderOpenApi = $renderOpenApi;
}
public function __invoke(Request $request, $area = 'default')
{
- if (!$this->generatorLocator->has($area)) {
+ try {
+ $response = new Response(
+ $this->renderOpenApi->render(RenderOpenApi::HTML, $area, [
+ 'server_url' => '' !== $request->getBaseUrl() ? $request->getSchemeAndHttpHost().$request->getBaseUrl() : null,
+ ]),
+ Response::HTTP_OK,
+ ['Content-Type' => 'text/html']
+ );
+
+ return $response->setCharset('UTF-8');
+ } catch (InvalidArgumentException $e) {
$advice = '';
if (false !== strpos($area, '.json')) {
$advice = ' Since the area provided contains `.json`, the issue is likely caused by route priorities. Try switching the Swagger UI / the json documentation routes order.';
@@ -45,23 +49,5 @@ final class SwaggerUiController
throw new BadRequestHttpException(sprintf('Area "%s" is not supported as it isn\'t defined in config.%s', $area, $advice));
}
-
- /** @var OpenApi $spec */
- $spec = $this->generatorLocator->get($area)->generate();
-
- if ('' !== $request->getBaseUrl()) {
- $spec->servers = [new Server(['url' => $request->getSchemeAndHttpHost().$request->getBaseUrl()])];
- }
-
- return new Response(
- $this->twig->render(
- '@NelmioApiDoc/SwaggerUi/index.html.twig',
- ['swagger_data' => ['spec' => json_decode($spec->toJson(), true)]]
- ),
- Response::HTTP_OK,
- ['Content-Type' => 'text/html']
- );
-
- return $response->setCharset('UTF-8');
}
}
diff --git a/Render/Html/HtmlOpenApiRenderer.php b/Render/Html/HtmlOpenApiRenderer.php
new file mode 100644
index 0000000..32417be
--- /dev/null
+++ b/Render/Html/HtmlOpenApiRenderer.php
@@ -0,0 +1,56 @@
+twig = $twig;
+ }
+
+ public function getFormat(): string
+ {
+ return RenderOpenApi::HTML;
+ }
+
+ public function render(OpenApi $spec, array $options = []): string
+ {
+ $options += [
+ 'server_url' => null,
+ ];
+
+ if ($options['server_url']) {
+ $spec->servers = [new Server(['url' => $options['server_url']])];
+ }
+
+ return $this->twig->render(
+ '@NelmioApiDoc/SwaggerUi/index.html.twig',
+ ['swagger_data' => ['spec' => json_decode($spec->toJson(), true)]]
+ );
+ }
+}
diff --git a/Render/Json/JsonOpenApiRenderer.php b/Render/Json/JsonOpenApiRenderer.php
new file mode 100644
index 0000000..09af9e1
--- /dev/null
+++ b/Render/Json/JsonOpenApiRenderer.php
@@ -0,0 +1,34 @@
+ false,
+ ];
+ $flags = $options['no-pretty'] ? 0 : JSON_PRETTY_PRINT;
+
+ return json_encode($spec, $flags);
+ }
+}
diff --git a/Render/OpenApiRenderer.php b/Render/OpenApiRenderer.php
new file mode 100644
index 0000000..5a672c4
--- /dev/null
+++ b/Render/OpenApiRenderer.php
@@ -0,0 +1,21 @@
+ */
+ private $openApiRenderers = [];
+
+ public function __construct(ContainerInterface $generatorLocator, OpenApiRenderer ...$openApiRenderers)
+ {
+ $this->generatorLocator = $generatorLocator;
+ foreach ($openApiRenderers as $openApiRenderer) {
+ $this->openApiRenderers[$openApiRenderer->getFormat()] = $openApiRenderer;
+ }
+ }
+
+ /**
+ * @throws InvalidArgumentException If the area to dump is not valid
+ */
+ public function render(string $format, string $area, array $options = []): string
+ {
+ if (!$this->generatorLocator->has($area)) {
+ throw new InvalidArgumentException(sprintf('Area "%s" is not supported.', $area));
+ } elseif (!array_key_exists($format, $this->openApiRenderers)) {
+ throw new InvalidArgumentException(sprintf('Format "%s" is not supported.', $format));
+ }
+
+ /** @var OpenApi $spec */
+ $spec = $this->generatorLocator->get($area)->generate();
+
+ return $this->openApiRenderers[$format]->render($spec, $options);
+ }
+}
diff --git a/Resources/config/services.xml b/Resources/config/services.xml
index 8c888a6..47174fd 100644
--- a/Resources/config/services.xml
+++ b/Resources/config/services.xml
@@ -6,14 +6,13 @@
-
+
-
-
+
@@ -26,6 +25,18 @@
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/Tests/Command/DumpCommandTest.php b/Tests/Command/DumpCommandTest.php
index 2ccb15e..76902d2 100644
--- a/Tests/Command/DumpCommandTest.php
+++ b/Tests/Command/DumpCommandTest.php
@@ -17,20 +17,35 @@ use Symfony\Component\Console\Tester\CommandTester;
class DumpCommandTest extends WebTestCase
{
- public function testExecute()
+ /** @dataProvider provideJsonMode */
+ public function testJson(array $jsonOptions, int $expectedJsonFlags)
+ {
+ $output = $this->executeDumpCommand($jsonOptions + [
+ '--area' => 'test',
+ ]);
+ $this->assertEquals(
+ json_encode($this->getOpenApiDefinition('test'), $expectedJsonFlags)."\n",
+ $output
+ );
+ }
+
+ public function provideJsonMode()
+ {
+ return [
+ 'pretty print' => [[], JSON_PRETTY_PRINT],
+ 'one line' => [['--no-pretty'], 0],
+ ];
+ }
+
+ private function executeDumpCommand(array $options)
{
$kernel = static::bootKernel();
$application = new Application($kernel);
$command = $application->find('nelmio:apidoc:dump');
$commandTester = new CommandTester($command);
- $commandTester->execute([
- '--area' => 'test',
- '--no-pretty' => '',
- ]);
+ $commandTester->execute($options);
- // the output of the command in the console
- $output = $commandTester->getDisplay();
- $this->assertEquals(json_encode($this->getOpenApiDefinition('test'))."\n", $output);
+ return $commandTester->getDisplay();
}
}
diff --git a/Tests/Render/RenderOpenApiTest.php b/Tests/Render/RenderOpenApiTest.php
new file mode 100644
index 0000000..d9b656b
--- /dev/null
+++ b/Tests/Render/RenderOpenApiTest.php
@@ -0,0 +1,75 @@
+createMock(OpenApiRenderer::class);
+ $openApiRenderer->method('getFormat')->willReturn($this->format);
+ $openApiRenderer->expects($this->once())->method('render');
+ $this->renderOpenApi($openApiRenderer);
+ }
+
+ public function testUnknownFormat()
+ {
+ $availableOpenApiRenderers = [];
+ $this->expectException(InvalidArgumentException::class);
+ $this->expectErrorMessage(sprintf('Format "%s" is not supported.', $this->format));
+ $this->renderOpenApi(...$availableOpenApiRenderers);
+ }
+
+ public function testUnknownArea()
+ {
+ $this->hasArea = false;
+ $this->expectException(InvalidArgumentException::class);
+ $this->expectErrorMessage(sprintf('Area "%s" is not supported.', $this->area));
+ $this->renderOpenApi();
+ }
+
+ private function renderOpenApi(...$openApiRenderer): void
+ {
+ $spec = $this->createMock(OpenApi::class);
+ $generator = new class($spec) {
+ private $spec;
+
+ public function __construct($spec)
+ {
+ $this->spec = $spec;
+ }
+
+ public function generate()
+ {
+ return $this->spec;
+ }
+ };
+
+ $generatorLocator = $this->createMock(ContainerInterface::class);
+ $generatorLocator->method('has')->willReturn($this->hasArea);
+ $generatorLocator->method('get')->willReturn($generator);
+
+ $renderOpenApi = new RenderOpenApi($generatorLocator, ...$openApiRenderer);
+ $renderOpenApi->render($this->format, $this->area, []);
+ }
+}
From 5124f07ece91d211c53730c4b689c1d20886b6bf Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Zdene=CC=8Ck=20Drahos=CC=8C?=
Date: Sun, 27 Jun 2021 09:24:35 +0200
Subject: [PATCH 2/9] Enable dumping html docs with cdn and offline assets
---
Command/DumpCommand.php | 37 ++++++++++++++---
Controller/SwaggerUiController.php | 2 +
Render/Html/AssetsMode.php | 19 +++++++++
Render/Html/GetNelmioAsset.php | 41 +++++++++++++++++++
Render/Html/HtmlOpenApiRenderer.php | 35 +++++++++++++---
Render/RenderOpenApi.php | 5 +++
Resources/config/services.xml | 1 +
Resources/doc/commands.rst | 39 ++++++++++++++++++
Resources/doc/index.rst | 1 +
Resources/views/SwaggerUi/index.html.twig | 38 ++++++++++++++----
Tests/Command/DumpCommandTest.php | 49 +++++++++++++++++++++++
11 files changed, 250 insertions(+), 17 deletions(-)
create mode 100644 Render/Html/AssetsMode.php
create mode 100644 Render/Html/GetNelmioAsset.php
create mode 100644 Resources/doc/commands.rst
diff --git a/Command/DumpCommand.php b/Command/DumpCommand.php
index b2438da..481c682 100644
--- a/Command/DumpCommand.php
+++ b/Command/DumpCommand.php
@@ -11,6 +11,7 @@
namespace Nelmio\ApiDocBundle\Command;
+use Nelmio\ApiDocBundle\Render\Html\AssetsMode;
use Nelmio\ApiDocBundle\Render\RenderOpenApi;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputInterface;
@@ -24,6 +25,15 @@ class DumpCommand extends Command
*/
private $renderOpenApi;
+ /**
+ * @var mixed[]
+ */
+ private $defaultHtmlConfig = [
+ 'assets_mode' => AssetsMode::CDN,
+ 'swagger_ui_config' => [],
+ 'server_url' => null,
+ ];
+
public function __construct(RenderOpenApi $renderOpenApi)
{
$this->renderOpenApi = $renderOpenApi;
@@ -36,9 +46,18 @@ class DumpCommand extends Command
*/
protected function configure()
{
+ $availableFormats = $this->renderOpenApi->getAvailableFormats();
$this
- ->setDescription('Dumps documentation in OpenAPI JSON format')
+ ->setDescription('Dumps documentation in OpenAPI JSON format or HTML')
->addOption('area', '', InputOption::VALUE_OPTIONAL, '', 'default')
+ ->addOption(
+ 'format',
+ '',
+ InputOption::VALUE_REQUIRED,
+ 'Output format like: '.implode(', ', $availableFormats),
+ RenderOpenApi::JSON
+ )
+ ->addOption('html-config', '', InputOption::VALUE_REQUIRED, '', json_encode($this->defaultHtmlConfig))
->addOption('no-pretty', '', InputOption::VALUE_NONE, 'Do not pretty format output')
;
}
@@ -49,11 +68,19 @@ class DumpCommand extends Command
protected function execute(InputInterface $input, OutputInterface $output)
{
$area = $input->getOption('area');
+ $format = $input->getOption('format');
- $options = [
- 'no-pretty' => $input->hasParameterOption(['--no-pretty']),
- ];
- $docs = $this->renderOpenApi->render(RenderOpenApi::JSON, $area, $options);
+ $options = [];
+ if (RenderOpenApi::HTML === $format) {
+ $rawHtmlConfig = json_decode($input->getOption('html-config'), true);
+ $options = is_array($rawHtmlConfig) ? $rawHtmlConfig : $this->defaultHtmlConfig;
+ } elseif (RenderOpenApi::JSON === $format) {
+ $options = [
+ 'no-pretty' => $input->hasParameterOption(['--no-pretty']),
+ ];
+ }
+
+ $docs = $this->renderOpenApi->render($format, $area, $options);
$output->writeln($docs, OutputInterface::OUTPUT_RAW);
return 0;
diff --git a/Controller/SwaggerUiController.php b/Controller/SwaggerUiController.php
index 4edad7f..1d4cca4 100644
--- a/Controller/SwaggerUiController.php
+++ b/Controller/SwaggerUiController.php
@@ -12,6 +12,7 @@
namespace Nelmio\ApiDocBundle\Controller;
use InvalidArgumentException;
+use Nelmio\ApiDocBundle\Render\Html\AssetsMode;
use Nelmio\ApiDocBundle\Render\RenderOpenApi;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
@@ -35,6 +36,7 @@ final class SwaggerUiController
$response = new Response(
$this->renderOpenApi->render(RenderOpenApi::HTML, $area, [
'server_url' => '' !== $request->getBaseUrl() ? $request->getSchemeAndHttpHost().$request->getBaseUrl() : null,
+ 'assets_mode' => AssetsMode::BUNDLE,
]),
Response::HTTP_OK,
['Content-Type' => 'text/html']
diff --git a/Render/Html/AssetsMode.php b/Render/Html/AssetsMode.php
new file mode 100644
index 0000000..2bbdf5a
--- /dev/null
+++ b/Render/Html/AssetsMode.php
@@ -0,0 +1,19 @@
+assetExtension = $assetExtension;
+ $this->defaultAssetsMode = $defaultAssetsMode;
+ }
+
+ public function __invoke($asset, $forcedMode = null)
+ {
+ $mode = $forcedMode ?: $this->defaultAssetsMode;
+ if (AssetsMode::CDN === $mode) {
+ return sprintf(
+ 'https://cdn.jsdelivr.net/gh/nelmio/NelmioApiDocBundle@4.1/Resources/public/%s',
+ $asset
+ );
+ } elseif (AssetsMode::OFFLINE === $mode) {
+ return file_get_contents(__DIR__.sprintf('/../../Resources/public/%s', $asset));
+ } else {
+ return $this->assetExtension->getAssetUrl(sprintf('bundles/nelmioapidoc/%s', $asset));
+ }
+ }
+}
diff --git a/Render/Html/HtmlOpenApiRenderer.php b/Render/Html/HtmlOpenApiRenderer.php
index 32417be..5bb8645 100644
--- a/Render/Html/HtmlOpenApiRenderer.php
+++ b/Render/Html/HtmlOpenApiRenderer.php
@@ -16,7 +16,9 @@ use Nelmio\ApiDocBundle\Render\OpenApiRenderer;
use Nelmio\ApiDocBundle\Render\RenderOpenApi;
use OpenApi\Annotations\OpenApi;
use OpenApi\Annotations\Server;
+use Symfony\Bridge\Twig\Extension\AssetExtension;
use Twig\Environment;
+use Twig\TwigFunction;
class HtmlOpenApiRenderer implements OpenApiRenderer
{
@@ -24,13 +26,18 @@ class HtmlOpenApiRenderer implements OpenApiRenderer
* @var Environment|\Twig_Environment
*/
private $twig;
+ /**
+ * @var AssetExtension
+ */
+ private $assetExtension;
- public function __construct($twig)
+ public function __construct($twig, AssetExtension $assetExtension)
{
if (!$twig instanceof \Twig_Environment && !$twig instanceof Environment) {
throw new InvalidArgumentException(sprintf('Providing an instance of "%s" as twig is not supported.', get_class($twig)));
}
$this->twig = $twig;
+ $this->assetExtension = $assetExtension;
}
public function getFormat(): string
@@ -42,15 +49,33 @@ class HtmlOpenApiRenderer implements OpenApiRenderer
{
$options += [
'server_url' => null,
+ 'assets_mode' => AssetsMode::CDN,
+ 'swagger_ui_config' => [],
];
- if ($options['server_url']) {
- $spec->servers = [new Server(['url' => $options['server_url']])];
- }
+ $this->twig->addFunction(
+ new TwigFunction(
+ 'nelmioAsset',
+ new GetNelmioAsset($this->assetExtension, $options['assets_mode'])
+ )
+ );
return $this->twig->render(
'@NelmioApiDoc/SwaggerUi/index.html.twig',
- ['swagger_data' => ['spec' => json_decode($spec->toJson(), true)]]
+ [
+ 'swagger_data' => ['spec' => $this->createJsonSpec($spec, $options['server_url'])],
+ 'assets_mode' => $options['assets_mode'],
+ 'swagger_ui_config' => $options['swagger_ui_config'],
+ ]
);
}
+
+ private function createJsonSpec(OpenApi $spec, $serverUrl)
+ {
+ if ($serverUrl) {
+ $spec->servers = [new Server(['url' => $serverUrl])];
+ }
+
+ return json_decode($spec->toJson(), true);
+ }
}
diff --git a/Render/RenderOpenApi.php b/Render/RenderOpenApi.php
index aace14f..1407b67 100644
--- a/Render/RenderOpenApi.php
+++ b/Render/RenderOpenApi.php
@@ -34,6 +34,11 @@ class RenderOpenApi
}
}
+ public function getAvailableFormats(): array
+ {
+ return array_keys($this->openApiRenderers);
+ }
+
/**
* @throws InvalidArgumentException If the area to dump is not valid
*/
diff --git a/Resources/config/services.xml b/Resources/config/services.xml
index 47174fd..e0127ae 100644
--- a/Resources/config/services.xml
+++ b/Resources/config/services.xml
@@ -33,6 +33,7 @@
+
diff --git a/Resources/doc/commands.rst b/Resources/doc/commands.rst
new file mode 100644
index 0000000..fe4df38
--- /dev/null
+++ b/Resources/doc/commands.rst
@@ -0,0 +1,39 @@
+Commands
+========
+
+A command is provided in order to dump the documentation in ``json`` or ``html``.
+
+.. code-block:: bash
+
+ $ php app/console api:doc:dump [--format="..."]
+
+The ``--format`` option allows to choose the format (default is: ``json``).
+
+By default, the generated JSON will be pretty-formatted. If you want to generate a json
+without whitespace, use the ``--no-pretty`` option.
+
+.. code-block:: bash
+
+ $ php app/console api:doc:dump --format=json > json-pretty-formatted.json
+ $ php app/console api:doc:dump --format=json --no-pretty > json-no-pretty.json
+
+For example to generate a static version of your documentation you can use:
+
+.. code-block:: bash
+
+ $ php app/console api:doc:dump --format=html > api.html
+
+By default, the generated HTML will add the sandbox feature.
+If you want to generate a static version of your documentation without sandbox,
+or configure UI configuration, use the ``--html-config`` option.
+
+- ``assets_mode`` - `cdn` loads assets from CDN, `offline` inlines assets
+- ``server_url`` - API url, useful if static documentation is not hosted on API url
+- ``swagger_ui_config`` - `configure Swagger UI`_
+ - ``"supportedSubmitMethods":[]`` disables the sandbox
+
+.. code-block:: bash
+
+ $ php app/console api:doc:dump --format=html --html-config '{"assets_mode":"offline","server_url":"https://example.com","swagger_ui_config":{"supportedSubmitMethods":[]}}' > api.html
+
+.. _`configure Swagger UI`: https://swagger.io/docs/open-source-tools/swagger-ui/usage/configuration/
diff --git a/Resources/doc/index.rst b/Resources/doc/index.rst
index fdc7c0e..9daa3ea 100644
--- a/Resources/doc/index.rst
+++ b/Resources/doc/index.rst
@@ -339,6 +339,7 @@ If you need more complex features, take a look at:
areas
alternative_names
customization
+ commands
faq
.. _`Symfony PropertyInfo component`: https://symfony.com/doc/current/components/property_info.html
diff --git a/Resources/views/SwaggerUi/index.html.twig b/Resources/views/SwaggerUi/index.html.twig
index 82e1332..0db7f8b 100644
--- a/Resources/views/SwaggerUi/index.html.twig
+++ b/Resources/views/SwaggerUi/index.html.twig
@@ -14,8 +14,13 @@ file that was distributed with this source code. #}
{% block title %}{{ swagger_data.spec.info.title }}{% endblock title %}
{% block stylesheets %}
-
-
+ {% if assets_mode == 'offline' %}
+
+
+ {% else %}
+
+
+ {% endif %}
{% endblock stylesheets %}
{% block swagger_data %}
@@ -53,7 +58,13 @@ file that was distributed with this source code. #}
{% endblock svg_icons %}
@@ -62,14 +73,27 @@ file that was distributed with this source code. #}
{% endblock %}
{% block javascripts %}
-
-
+ {% if assets_mode == 'offline' %}
+
+
+ {% else %}
+
+
+ {% endif %}
{% endblock javascripts %}
-
+ {% if assets_mode == 'offline' %}
+
+ {% else %}
+
+ {% endif %}
+
{% block swagger_initialization %}
{% endblock swagger_initialization %}
', $output);
+ self::assertStringContainsString($expectedHtml, $output);
+ }
+
+ public function provideAssetsMode()
+ {
+ return [
+ 'default mode is cdn' => [
+ null,
+ 'https://cdn.jsdelivr.net',
+ ],
+ 'invalid mode fallbacks to cdn' => [
+ 'invalid',
+ 'https://cdn.jsdelivr.net',
+ ],
+ 'select cdn mode' => [
+ ['assets_mode' => AssetsMode::CDN],
+ 'https://cdn.jsdelivr.net',
+ ],
+ 'select offline mode' => [
+ ['assets_mode' => AssetsMode::OFFLINE],
+ '', $stylesheet);
+ } else {
+ return sprintf('', $stylesheet);
}
}
}
diff --git a/Render/Html/HtmlOpenApiRenderer.php b/Render/Html/HtmlOpenApiRenderer.php
index 5bb8645..bedf14e 100644
--- a/Render/Html/HtmlOpenApiRenderer.php
+++ b/Render/Html/HtmlOpenApiRenderer.php
@@ -16,28 +16,23 @@ use Nelmio\ApiDocBundle\Render\OpenApiRenderer;
use Nelmio\ApiDocBundle\Render\RenderOpenApi;
use OpenApi\Annotations\OpenApi;
use OpenApi\Annotations\Server;
-use Symfony\Bridge\Twig\Extension\AssetExtension;
use Twig\Environment;
-use Twig\TwigFunction;
class HtmlOpenApiRenderer implements OpenApiRenderer
{
- /**
- * @var Environment|\Twig_Environment
- */
+ /** @var Environment|\Twig_Environment */
private $twig;
- /**
- * @var AssetExtension
- */
- private $assetExtension;
- public function __construct($twig, AssetExtension $assetExtension)
+ /** @var GetNelmioAsset */
+ private $getNelmioAsset;
+
+ public function __construct($twig, GetNelmioAsset $getNelmioAsset)
{
if (!$twig instanceof \Twig_Environment && !$twig instanceof Environment) {
throw new InvalidArgumentException(sprintf('Providing an instance of "%s" as twig is not supported.', get_class($twig)));
}
$this->twig = $twig;
- $this->assetExtension = $assetExtension;
+ $this->getNelmioAsset = $getNelmioAsset;
}
public function getFormat(): string
@@ -53,12 +48,7 @@ class HtmlOpenApiRenderer implements OpenApiRenderer
'swagger_ui_config' => [],
];
- $this->twig->addFunction(
- new TwigFunction(
- 'nelmioAsset',
- new GetNelmioAsset($this->assetExtension, $options['assets_mode'])
- )
- );
+ $this->twig->addFunction($this->getNelmioAsset->toTwigFunction($options['assets_mode']));
return $this->twig->render(
'@NelmioApiDoc/SwaggerUi/index.html.twig',
diff --git a/Resources/config/services.xml b/Resources/config/services.xml
index e0127ae..d125a10 100644
--- a/Resources/config/services.xml
+++ b/Resources/config/services.xml
@@ -33,6 +33,9 @@
+
+
+
diff --git a/Resources/views/SwaggerUi/index.html.twig b/Resources/views/SwaggerUi/index.html.twig
index 0db7f8b..a1b3129 100644
--- a/Resources/views/SwaggerUi/index.html.twig
+++ b/Resources/views/SwaggerUi/index.html.twig
@@ -14,13 +14,8 @@ file that was distributed with this source code. #}
{% block title %}{{ swagger_data.spec.info.title }}{% endblock title %}
{% block stylesheets %}
- {% if assets_mode == 'offline' %}
-
-
- {% else %}
-
-
- {% endif %}
+ {{ nelmioAsset('swagger-ui/swagger-ui.css') }}
+ {{ nelmioAsset('style.css') }}
{% endblock stylesheets %}
{% block swagger_data %}
@@ -59,11 +54,7 @@ file that was distributed with this source code. #}
@@ -73,20 +64,11 @@ file that was distributed with this source code. #}
{% endblock %}
{% block javascripts %}
- {% if assets_mode == 'offline' %}
-
-
- {% else %}
-
-
- {% endif %}
+ {{ nelmioAsset('swagger-ui/swagger-ui-bundle.js') }}
+ {{ nelmioAsset('swagger-ui/swagger-ui-standalone-preset.js') }}
{% endblock javascripts %}
- {% if assets_mode == 'offline' %}
-
- {% else %}
-
- {% endif %}
+ {{ nelmioAsset('init-swagger-ui.js') }}
{% block swagger_initialization %}
',
+ ],
+ 'cdn js' => [
+ AssetsMode::CDN,
+ 'init-swagger-ui.js',
+ '',
+ ],
+ 'offline js' => [
+ AssetsMode::OFFLINE,
+ 'init-swagger-ui.js',
+ '',
+ ],
+ 'external js' => [
+ AssetsMode::BUNDLE,
+ 'https://cdn.com/my.js',
+ '',
+ ],
+ ];
+ }
+
+ private function provideImage($cdnDir)
+ {
+ return [
+ 'bundled image' => [
+ AssetsMode::BUNDLE,
+ 'logo.png',
+ '/bundles/nelmioapidoc/logo.png',
+ ],
+ 'cdn image' => [
+ AssetsMode::CDN,
+ 'logo.png',
+ $cdnDir.'/logo.png',
+ ],
+ 'offline image fallbacks to cdn' => [
+ AssetsMode::OFFLINE,
+ 'logo.png',
+ $cdnDir.'/logo.png',
+ ],
+ ];
+ }
+}
From 2c890ff93b58496ab1139ec4ad6fc7942e7f7584 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Zdene=CC=8Ck=20Drahos=CC=8C?=
Date: Sat, 10 Jul 2021 18:00:11 +0200
Subject: [PATCH 4/9] Hotfix testing private service in Symfony < 5.3
https://github.com/nelmio/NelmioApiDocBundle/runs/3031148906?check_suite_focus=true#step:7:106
Error: Call to undefined method Nelmio\ApiDocBundle\Tests\Render\Html\GetNelmioAssetTest::getContainer()
https://symfony.com/blog/new-in-symfony-4-1-simpler-service-testing
In practice, tests based on WebTestCase and KernelTestCase now access to a special container via the static::$container property that allows fetching non-removed private services:
https://github.com/symfony/symfony/blob/5.3/CHANGELOG-5.3.md#changelog-for-53x
feature #40366 [FrameworkBundle] Add KernelTestCase::getContainer() (Nyholm)
static $container @deprecated since Symfony 5.3, use static::getContainer() instead
---
Tests/Render/Html/GetNelmioAssetTest.php | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/Tests/Render/Html/GetNelmioAssetTest.php b/Tests/Render/Html/GetNelmioAssetTest.php
index 2077a11..ce33be5 100644
--- a/Tests/Render/Html/GetNelmioAssetTest.php
+++ b/Tests/Render/Html/GetNelmioAssetTest.php
@@ -22,7 +22,7 @@ class GetNelmioAssetTest extends WebTestCase
{
static::bootKernel();
/** @var GetNelmioAsset $getNelmioAsset */
- $getNelmioAsset = self::getContainer()->get('nelmio_api_doc.render_docs.html.asset');
+ $getNelmioAsset = static::$container->get('nelmio_api_doc.render_docs.html.asset');
$twigFunction = $getNelmioAsset->toTwigFunction($mode);
self::assertSame($expectedContent, $twigFunction->getCallable()->__invoke($asset, $mode));
}
From 1b9be28ad69d4cfedd5fa8424c5727ff19d0a93b Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Zdene=CC=8Ck=20Drahos=CC=8C?=
Date: Thu, 29 Jul 2021 11:57:00 +0200
Subject: [PATCH 5/9] Enable dumping docs to yaml
---
Command/DumpCommand.php | 2 +-
Controller/YamlDocumentationController.php | 37 +++++++++++----------
Render/RenderOpenApi.php | 1 +
Render/Yaml/YamlOpenApiRenderer.php | 38 ++++++++++++++++++++++
Resources/config/services.xml | 5 ++-
Resources/doc/commands.rst | 2 +-
Tests/Command/DumpCommandTest.php | 8 +++++
7 files changed, 72 insertions(+), 21 deletions(-)
create mode 100644 Render/Yaml/YamlOpenApiRenderer.php
diff --git a/Command/DumpCommand.php b/Command/DumpCommand.php
index 481c682..47f80be 100644
--- a/Command/DumpCommand.php
+++ b/Command/DumpCommand.php
@@ -48,7 +48,7 @@ class DumpCommand extends Command
{
$availableFormats = $this->renderOpenApi->getAvailableFormats();
$this
- ->setDescription('Dumps documentation in OpenAPI JSON format or HTML')
+ ->setDescription('Dumps documentation in OpenAPI format to: '.implode(', ', $availableFormats))
->addOption('area', '', InputOption::VALUE_OPTIONAL, '', 'default')
->addOption(
'format',
diff --git a/Controller/YamlDocumentationController.php b/Controller/YamlDocumentationController.php
index 7557fc8..ef9bc36 100644
--- a/Controller/YamlDocumentationController.php
+++ b/Controller/YamlDocumentationController.php
@@ -11,37 +11,38 @@
namespace Nelmio\ApiDocBundle\Controller;
-use OpenApi\Annotations\OpenApi;
-use OpenApi\Annotations\Server;
-use Psr\Container\ContainerInterface;
+use InvalidArgumentException;
+use Nelmio\ApiDocBundle\Render\RenderOpenApi;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpKernel\Exception\BadRequestHttpException;
final class YamlDocumentationController
{
- private $generatorLocator;
+ /**
+ * @var RenderOpenApi
+ */
+ private $renderOpenApi;
- public function __construct(ContainerInterface $generatorLocator)
+ public function __construct(RenderOpenApi $renderOpenApi)
{
- $this->generatorLocator = $generatorLocator;
+ $this->renderOpenApi = $renderOpenApi;
}
public function __invoke(Request $request, $area = 'default')
{
- if (!$this->generatorLocator->has($area)) {
+ try {
+ $response = new Response(
+ $this->renderOpenApi->render(RenderOpenApi::YAML, $area, [
+ 'server_url' => '' !== $request->getBaseUrl() ? $request->getSchemeAndHttpHost().$request->getBaseUrl() : null,
+ ]),
+ Response::HTTP_OK,
+ ['Content-Type' => 'text/x-yaml']
+ );
+
+ return $response->setCharset('UTF-8');
+ } catch (InvalidArgumentException $e) {
throw new BadRequestHttpException(sprintf('Area "%s" is not supported as it isn\'t defined in config.', $area));
}
-
- /** @var OpenApi $spec */
- $spec = $this->generatorLocator->get($area)->generate();
-
- if ('' !== $request->getBaseUrl()) {
- $spec->servers = [new Server(['url' => $request->getSchemeAndHttpHost().$request->getBaseUrl()])];
- }
-
- return new Response($spec->toYaml(), 200, [
- 'Content-Type' => 'text/x-yaml',
- ]);
}
}
diff --git a/Render/RenderOpenApi.php b/Render/RenderOpenApi.php
index 1407b67..8076d49 100644
--- a/Render/RenderOpenApi.php
+++ b/Render/RenderOpenApi.php
@@ -19,6 +19,7 @@ class RenderOpenApi
{
public const HTML = 'html';
public const JSON = 'json';
+ public const YAML = 'yaml';
/** @var ContainerInterface */
private $generatorLocator;
diff --git a/Render/Yaml/YamlOpenApiRenderer.php b/Render/Yaml/YamlOpenApiRenderer.php
new file mode 100644
index 0000000..025216c
--- /dev/null
+++ b/Render/Yaml/YamlOpenApiRenderer.php
@@ -0,0 +1,38 @@
+ null,
+ ];
+
+ if ($options['server_url']) {
+ $spec->servers = [new Server(['url' => $options['server_url']])];
+ }
+
+ return $spec->toYaml();
+ }
+}
diff --git a/Resources/config/services.xml b/Resources/config/services.xml
index d125a10..d452034 100644
--- a/Resources/config/services.xml
+++ b/Resources/config/services.xml
@@ -22,7 +22,7 @@
-
+
@@ -30,6 +30,7 @@
+
@@ -40,6 +41,8 @@
+
+
diff --git a/Resources/doc/commands.rst b/Resources/doc/commands.rst
index fe4df38..93bb6e1 100644
--- a/Resources/doc/commands.rst
+++ b/Resources/doc/commands.rst
@@ -1,7 +1,7 @@
Commands
========
-A command is provided in order to dump the documentation in ``json`` or ``html``.
+A command is provided in order to dump the documentation in ``json``, ``yaml`` or ``html``.
.. code-block:: bash
diff --git a/Tests/Command/DumpCommandTest.php b/Tests/Command/DumpCommandTest.php
index 23e6f3a..eb2bfbe 100644
--- a/Tests/Command/DumpCommandTest.php
+++ b/Tests/Command/DumpCommandTest.php
@@ -38,6 +38,14 @@ class DumpCommandTest extends WebTestCase
];
}
+ public function testYaml()
+ {
+ $output = $this->executeDumpCommand([
+ '--format' => 'yaml',
+ ]);
+ self::assertStringContainsString($this->getOpenApiDefinition()->toYaml(), $output);
+ }
+
/** @dataProvider provideAssetsMode */
public function testHtml($htmlConfig, string $expectedHtml)
{
From 1f29be85f4cfd61427d407f4dc49ad673d1ec26a Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Zdene=CC=8Ck=20Drahos=CC=8C?=
Date: Thu, 29 Jul 2021 12:01:54 +0200
Subject: [PATCH 6/9] Use json renderer in controller
---
Controller/DocumentationController.php | 30 ++++++++++++--------------
Render/Json/JsonOpenApiRenderer.php | 6 ++++++
Resources/config/services.xml | 2 +-
3 files changed, 21 insertions(+), 17 deletions(-)
diff --git a/Controller/DocumentationController.php b/Controller/DocumentationController.php
index 0450d3a..4374bb2 100644
--- a/Controller/DocumentationController.php
+++ b/Controller/DocumentationController.php
@@ -11,35 +11,33 @@
namespace Nelmio\ApiDocBundle\Controller;
-use OpenApi\Annotations\OpenApi;
-use OpenApi\Annotations\Server;
-use Psr\Container\ContainerInterface;
+use Nelmio\ApiDocBundle\Render\RenderOpenApi;
use Symfony\Component\HttpFoundation\JsonResponse;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpKernel\Exception\BadRequestHttpException;
final class DocumentationController
{
- private $generatorLocator;
+ /**
+ * @var RenderOpenApi
+ */
+ private $renderOpenApi;
- public function __construct(ContainerInterface $generatorLocator)
+ public function __construct(RenderOpenApi $renderOpenApi)
{
- $this->generatorLocator = $generatorLocator;
+ $this->renderOpenApi = $renderOpenApi;
}
public function __invoke(Request $request, $area = 'default')
{
- if (!$this->generatorLocator->has($area)) {
+ try {
+ return JsonResponse::fromJsonString(
+ $this->renderOpenApi->render(RenderOpenApi::JSON, $area, [
+ 'server_url' => '' !== $request->getBaseUrl() ? $request->getSchemeAndHttpHost().$request->getBaseUrl() : null,
+ ])
+ );
+ } catch (InvalidArgumentException $e) {
throw new BadRequestHttpException(sprintf('Area "%s" is not supported as it isn\'t defined in config.', $area));
}
-
- /** @var OpenApi $spec */
- $spec = $this->generatorLocator->get($area)->generate();
-
- if ('' !== $request->getBaseUrl()) {
- $spec->servers = [new Server(['url' => $request->getSchemeAndHttpHost().$request->getBaseUrl()])];
- }
-
- return new JsonResponse($spec);
}
}
diff --git a/Render/Json/JsonOpenApiRenderer.php b/Render/Json/JsonOpenApiRenderer.php
index 09af9e1..a5da3c9 100644
--- a/Render/Json/JsonOpenApiRenderer.php
+++ b/Render/Json/JsonOpenApiRenderer.php
@@ -14,6 +14,7 @@ namespace Nelmio\ApiDocBundle\Render\Json;
use Nelmio\ApiDocBundle\Render\OpenApiRenderer;
use Nelmio\ApiDocBundle\Render\RenderOpenApi;
use OpenApi\Annotations\OpenApi;
+use OpenApi\Annotations\Server;
class JsonOpenApiRenderer implements OpenApiRenderer
{
@@ -25,10 +26,15 @@ class JsonOpenApiRenderer implements OpenApiRenderer
public function render(OpenApi $spec, array $options = []): string
{
$options += [
+ 'server_url' => null,
'no-pretty' => false,
];
$flags = $options['no-pretty'] ? 0 : JSON_PRETTY_PRINT;
+ if ($options['server_url']) {
+ $spec->servers = [new Server(['url' => $options['server_url']])];
+ }
+
return json_encode($spec, $flags);
}
}
diff --git a/Resources/config/services.xml b/Resources/config/services.xml
index d452034..3caaba4 100644
--- a/Resources/config/services.xml
+++ b/Resources/config/services.xml
@@ -18,7 +18,7 @@
-
+
From 181f4b276356f34ea307b5d44ec7ff6cc189ea27 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Zdene=CC=8Ck=20Drahos=CC=8C?=
Date: Thu, 29 Jul 2021 12:11:10 +0200
Subject: [PATCH 7/9] Keep server_url from Request only in RenderOpenApi
---
Controller/DocumentationController.php | 4 +---
Controller/SwaggerUiController.php | 3 +--
Controller/YamlDocumentationController.php | 4 +---
Render/Html/HtmlOpenApiRenderer.php | 13 +------------
Render/Json/JsonOpenApiRenderer.php | 6 ------
Render/RenderOpenApi.php | 19 +++++++++++++++++++
Render/Yaml/YamlOpenApiRenderer.php | 9 ---------
7 files changed, 23 insertions(+), 35 deletions(-)
diff --git a/Controller/DocumentationController.php b/Controller/DocumentationController.php
index 4374bb2..020451a 100644
--- a/Controller/DocumentationController.php
+++ b/Controller/DocumentationController.php
@@ -32,9 +32,7 @@ final class DocumentationController
{
try {
return JsonResponse::fromJsonString(
- $this->renderOpenApi->render(RenderOpenApi::JSON, $area, [
- 'server_url' => '' !== $request->getBaseUrl() ? $request->getSchemeAndHttpHost().$request->getBaseUrl() : null,
- ])
+ $this->renderOpenApi->renderFromRequest($request, RenderOpenApi::JSON, $area)
);
} catch (InvalidArgumentException $e) {
throw new BadRequestHttpException(sprintf('Area "%s" is not supported as it isn\'t defined in config.', $area));
diff --git a/Controller/SwaggerUiController.php b/Controller/SwaggerUiController.php
index 1d4cca4..c5a3201 100644
--- a/Controller/SwaggerUiController.php
+++ b/Controller/SwaggerUiController.php
@@ -34,8 +34,7 @@ final class SwaggerUiController
{
try {
$response = new Response(
- $this->renderOpenApi->render(RenderOpenApi::HTML, $area, [
- 'server_url' => '' !== $request->getBaseUrl() ? $request->getSchemeAndHttpHost().$request->getBaseUrl() : null,
+ $this->renderOpenApi->renderFromRequest($request, RenderOpenApi::HTML, $area, [
'assets_mode' => AssetsMode::BUNDLE,
]),
Response::HTTP_OK,
diff --git a/Controller/YamlDocumentationController.php b/Controller/YamlDocumentationController.php
index ef9bc36..a935a2d 100644
--- a/Controller/YamlDocumentationController.php
+++ b/Controller/YamlDocumentationController.php
@@ -33,9 +33,7 @@ final class YamlDocumentationController
{
try {
$response = new Response(
- $this->renderOpenApi->render(RenderOpenApi::YAML, $area, [
- 'server_url' => '' !== $request->getBaseUrl() ? $request->getSchemeAndHttpHost().$request->getBaseUrl() : null,
- ]),
+ $this->renderOpenApi->renderFromRequest($request, RenderOpenApi::YAML, $area),
Response::HTTP_OK,
['Content-Type' => 'text/x-yaml']
);
diff --git a/Render/Html/HtmlOpenApiRenderer.php b/Render/Html/HtmlOpenApiRenderer.php
index bedf14e..88cb7d1 100644
--- a/Render/Html/HtmlOpenApiRenderer.php
+++ b/Render/Html/HtmlOpenApiRenderer.php
@@ -15,7 +15,6 @@ use InvalidArgumentException;
use Nelmio\ApiDocBundle\Render\OpenApiRenderer;
use Nelmio\ApiDocBundle\Render\RenderOpenApi;
use OpenApi\Annotations\OpenApi;
-use OpenApi\Annotations\Server;
use Twig\Environment;
class HtmlOpenApiRenderer implements OpenApiRenderer
@@ -43,7 +42,6 @@ class HtmlOpenApiRenderer implements OpenApiRenderer
public function render(OpenApi $spec, array $options = []): string
{
$options += [
- 'server_url' => null,
'assets_mode' => AssetsMode::CDN,
'swagger_ui_config' => [],
];
@@ -53,19 +51,10 @@ class HtmlOpenApiRenderer implements OpenApiRenderer
return $this->twig->render(
'@NelmioApiDoc/SwaggerUi/index.html.twig',
[
- 'swagger_data' => ['spec' => $this->createJsonSpec($spec, $options['server_url'])],
+ 'swagger_data' => ['spec' => json_decode($spec->toJson(), true)],
'assets_mode' => $options['assets_mode'],
'swagger_ui_config' => $options['swagger_ui_config'],
]
);
}
-
- private function createJsonSpec(OpenApi $spec, $serverUrl)
- {
- if ($serverUrl) {
- $spec->servers = [new Server(['url' => $serverUrl])];
- }
-
- return json_decode($spec->toJson(), true);
- }
}
diff --git a/Render/Json/JsonOpenApiRenderer.php b/Render/Json/JsonOpenApiRenderer.php
index a5da3c9..09af9e1 100644
--- a/Render/Json/JsonOpenApiRenderer.php
+++ b/Render/Json/JsonOpenApiRenderer.php
@@ -14,7 +14,6 @@ namespace Nelmio\ApiDocBundle\Render\Json;
use Nelmio\ApiDocBundle\Render\OpenApiRenderer;
use Nelmio\ApiDocBundle\Render\RenderOpenApi;
use OpenApi\Annotations\OpenApi;
-use OpenApi\Annotations\Server;
class JsonOpenApiRenderer implements OpenApiRenderer
{
@@ -26,15 +25,10 @@ class JsonOpenApiRenderer implements OpenApiRenderer
public function render(OpenApi $spec, array $options = []): string
{
$options += [
- 'server_url' => null,
'no-pretty' => false,
];
$flags = $options['no-pretty'] ? 0 : JSON_PRETTY_PRINT;
- if ($options['server_url']) {
- $spec->servers = [new Server(['url' => $options['server_url']])];
- }
-
return json_encode($spec, $flags);
}
}
diff --git a/Render/RenderOpenApi.php b/Render/RenderOpenApi.php
index 8076d49..46b1ae1 100644
--- a/Render/RenderOpenApi.php
+++ b/Render/RenderOpenApi.php
@@ -13,7 +13,9 @@ namespace Nelmio\ApiDocBundle\Render;
use InvalidArgumentException;
use OpenApi\Annotations\OpenApi;
+use OpenApi\Annotations\Server;
use Psr\Container\ContainerInterface;
+use Symfony\Component\HttpFoundation\Request;
class RenderOpenApi
{
@@ -40,6 +42,19 @@ class RenderOpenApi
return array_keys($this->openApiRenderers);
}
+ public function renderFromRequest(Request $request, string $format, $area, array $extraOptions = [])
+ {
+ $options = [];
+ if ('' !== $request->getBaseUrl()) {
+ $options += [
+ 'server_url' => $request->getSchemeAndHttpHost().$request->getBaseUrl(),
+ ];
+ }
+ $options += $extraOptions;
+
+ return $this->render($format, $area, $options);
+ }
+
/**
* @throws InvalidArgumentException If the area to dump is not valid
*/
@@ -54,6 +69,10 @@ class RenderOpenApi
/** @var OpenApi $spec */
$spec = $this->generatorLocator->get($area)->generate();
+ if (array_key_exists('server_url', $options) && $options['server_url']) {
+ $spec->servers = [new Server(['url' => $options['server_url']])];
+ }
+
return $this->openApiRenderers[$format]->render($spec, $options);
}
}
diff --git a/Render/Yaml/YamlOpenApiRenderer.php b/Render/Yaml/YamlOpenApiRenderer.php
index 025216c..a8e7c5d 100644
--- a/Render/Yaml/YamlOpenApiRenderer.php
+++ b/Render/Yaml/YamlOpenApiRenderer.php
@@ -14,7 +14,6 @@ namespace Nelmio\ApiDocBundle\Render\Yaml;
use Nelmio\ApiDocBundle\Render\OpenApiRenderer;
use Nelmio\ApiDocBundle\Render\RenderOpenApi;
use OpenApi\Annotations\OpenApi;
-use OpenApi\Annotations\Server;
class YamlOpenApiRenderer implements OpenApiRenderer
{
@@ -25,14 +24,6 @@ class YamlOpenApiRenderer implements OpenApiRenderer
public function render(OpenApi $spec, array $options = []): string
{
- $options += [
- 'server_url' => null,
- ];
-
- if ($options['server_url']) {
- $spec->servers = [new Server(['url' => $options['server_url']])];
- }
-
return $spec->toYaml();
}
}
From 1a21f1855ed9866e512aa59f3fd4d3981f1d8a4d Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Zdene=CC=8Ck=20Drahos=CC=8C?=
Date: Thu, 29 Jul 2021 12:22:54 +0200
Subject: [PATCH 8/9] Enable overriding server url for yaml and json export
from console
---
Command/DumpCommand.php | 8 ++++++--
Resources/doc/commands.rst | 6 ++++++
Tests/Command/DumpCommandTest.php | 8 +++++++-
3 files changed, 19 insertions(+), 3 deletions(-)
diff --git a/Command/DumpCommand.php b/Command/DumpCommand.php
index 47f80be..c2d929f 100644
--- a/Command/DumpCommand.php
+++ b/Command/DumpCommand.php
@@ -31,7 +31,6 @@ class DumpCommand extends Command
private $defaultHtmlConfig = [
'assets_mode' => AssetsMode::CDN,
'swagger_ui_config' => [],
- 'server_url' => null,
];
public function __construct(RenderOpenApi $renderOpenApi)
@@ -57,8 +56,9 @@ class DumpCommand extends Command
'Output format like: '.implode(', ', $availableFormats),
RenderOpenApi::JSON
)
+ ->addOption('server-url', '', InputOption::VALUE_REQUIRED, 'URL where live api doc is served')
->addOption('html-config', '', InputOption::VALUE_REQUIRED, '', json_encode($this->defaultHtmlConfig))
- ->addOption('no-pretty', '', InputOption::VALUE_NONE, 'Do not pretty format output')
+ ->addOption('no-pretty', '', InputOption::VALUE_NONE, 'Do not pretty format JSON output')
;
}
@@ -80,6 +80,10 @@ class DumpCommand extends Command
];
}
+ if ($input->getOption('server-url')) {
+ $options['server_url'] = $input->getOption('server-url');
+ }
+
$docs = $this->renderOpenApi->render($format, $area, $options);
$output->writeln($docs, OutputInterface::OUTPUT_RAW);
diff --git a/Resources/doc/commands.rst b/Resources/doc/commands.rst
index 93bb6e1..839a122 100644
--- a/Resources/doc/commands.rst
+++ b/Resources/doc/commands.rst
@@ -17,6 +17,12 @@ without whitespace, use the ``--no-pretty`` option.
$ php app/console api:doc:dump --format=json > json-pretty-formatted.json
$ php app/console api:doc:dump --format=json --no-pretty > json-no-pretty.json
+Every format can override API url. Useful if static documentation is not hosted on API url:
+
+.. code-block:: bash
+
+ $ php app/console api:doc:dump --format=yaml --server-url "http://example.com/api" > api.yaml
+
For example to generate a static version of your documentation you can use:
.. code-block:: bash
diff --git a/Tests/Command/DumpCommandTest.php b/Tests/Command/DumpCommandTest.php
index eb2bfbe..9d82f38 100644
--- a/Tests/Command/DumpCommandTest.php
+++ b/Tests/Command/DumpCommandTest.php
@@ -42,8 +42,14 @@ class DumpCommandTest extends WebTestCase
{
$output = $this->executeDumpCommand([
'--format' => 'yaml',
+ '--server-url' => 'http://example.com/api',
]);
- self::assertStringContainsString($this->getOpenApiDefinition()->toYaml(), $output);
+ $expectedYaml = <<
Date: Sat, 28 Aug 2021 08:29:07 +0200
Subject: [PATCH 9/9] Add @internal to renderer classes
---
Render/Html/GetNelmioAsset.php | 3 +++
Render/Html/HtmlOpenApiRenderer.php | 3 +++
Render/Json/JsonOpenApiRenderer.php | 3 +++
Render/OpenApiRenderer.php | 3 +++
Render/Yaml/YamlOpenApiRenderer.php | 3 +++
5 files changed, 15 insertions(+)
diff --git a/Render/Html/GetNelmioAsset.php b/Render/Html/GetNelmioAsset.php
index 38e85a6..a312899 100644
--- a/Render/Html/GetNelmioAsset.php
+++ b/Render/Html/GetNelmioAsset.php
@@ -14,6 +14,9 @@ namespace Nelmio\ApiDocBundle\Render\Html;
use Symfony\Bridge\Twig\Extension\AssetExtension;
use Twig\TwigFunction;
+/**
+ * @internal
+ */
class GetNelmioAsset
{
private $assetExtension;
diff --git a/Render/Html/HtmlOpenApiRenderer.php b/Render/Html/HtmlOpenApiRenderer.php
index 88cb7d1..78577f1 100644
--- a/Render/Html/HtmlOpenApiRenderer.php
+++ b/Render/Html/HtmlOpenApiRenderer.php
@@ -17,6 +17,9 @@ use Nelmio\ApiDocBundle\Render\RenderOpenApi;
use OpenApi\Annotations\OpenApi;
use Twig\Environment;
+/**
+ * @internal
+ */
class HtmlOpenApiRenderer implements OpenApiRenderer
{
/** @var Environment|\Twig_Environment */
diff --git a/Render/Json/JsonOpenApiRenderer.php b/Render/Json/JsonOpenApiRenderer.php
index 09af9e1..62b0a7f 100644
--- a/Render/Json/JsonOpenApiRenderer.php
+++ b/Render/Json/JsonOpenApiRenderer.php
@@ -15,6 +15,9 @@ use Nelmio\ApiDocBundle\Render\OpenApiRenderer;
use Nelmio\ApiDocBundle\Render\RenderOpenApi;
use OpenApi\Annotations\OpenApi;
+/**
+ * @internal
+ */
class JsonOpenApiRenderer implements OpenApiRenderer
{
public function getFormat(): string
diff --git a/Render/OpenApiRenderer.php b/Render/OpenApiRenderer.php
index 5a672c4..73edb66 100644
--- a/Render/OpenApiRenderer.php
+++ b/Render/OpenApiRenderer.php
@@ -13,6 +13,9 @@ namespace Nelmio\ApiDocBundle\Render;
use OpenApi\Annotations\OpenApi;
+/**
+ * @internal
+ */
interface OpenApiRenderer
{
public function getFormat(): string;
diff --git a/Render/Yaml/YamlOpenApiRenderer.php b/Render/Yaml/YamlOpenApiRenderer.php
index a8e7c5d..b96a284 100644
--- a/Render/Yaml/YamlOpenApiRenderer.php
+++ b/Render/Yaml/YamlOpenApiRenderer.php
@@ -15,6 +15,9 @@ use Nelmio\ApiDocBundle\Render\OpenApiRenderer;
use Nelmio\ApiDocBundle\Render\RenderOpenApi;
use OpenApi\Annotations\OpenApi;
+/**
+ * @internal
+ */
class YamlOpenApiRenderer implements OpenApiRenderer
{
public function getFormat(): string