Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: Add ability to configure UI through configuration #2251

Merged
merged 12 commits into from
Apr 19, 2024
14 changes: 14 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,20 @@
CHANGELOG
=========

4.26.0
-----
* Add ability to configure UI through configuration
```yaml
nelmio_api_doc:
html_config:
assets_mode: bundle
redocly_config:
expandResponses: '200,201'
hideDownloadButton: true
swagger_ui_config:
deepLinking: true
```

4.25.0
-----
* Added support for [JMS @Discriminator](https://jmsyst.com/libs/serializer/master/reference/annotations#discriminator) annotation/attribute
Expand Down
1 change: 1 addition & 0 deletions config/services.xml
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@
</service>
<service id="nelmio_api_doc.render_docs.html" class="Nelmio\ApiDocBundle\Render\Html\HtmlOpenApiRenderer" public="false">
<argument type="service" id="twig" />
<argument type="collection" />
</service>
<service id="nelmio_api_doc.render_docs.html.asset" class="Nelmio\ApiDocBundle\Render\Html\GetNelmioAsset" public="false">
<argument type="service" id="twig.extension.assets" />
Expand Down
2 changes: 2 additions & 0 deletions docs/commands.rst
Original file line number Diff line number Diff line change
Expand Up @@ -37,9 +37,11 @@ or configure UI configuration, use the ``--html-config`` option.
- ``server_url`` - API url, useful if static documentation is not hosted on API url
- ``swagger_ui_config`` - `configure Swagger UI`_
- ``"supportedSubmitMethods":[]`` disables the sandbox
- ``redocly_config`` - `configure Redocly`_

.. code-block:: bash

$ php bin/console nelmio:apidoc: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/
.. _`configure Redocly`: https://redocly.com/docs/redoc/config/
28 changes: 23 additions & 5 deletions docs/customization.rst
Original file line number Diff line number Diff line change
Expand Up @@ -25,16 +25,34 @@ Just create a file ``templates/bundles/NelmioApiDocBundle/SwaggerUi/index.html.t
{% extends '@!NelmioApiDoc/SwaggerUi/index.html.twig' %}

{#
Change swagger UI configuration
Change Swagger UI configuration
All parameters are explained on Swagger UI website:
https://swagger.io/docs/open-source-tools/swagger-ui/usage/configuration/
#}
{% block swagger_initialization %}
<script type="text/javascript">
window.onload = loadSwaggerUI({
defaultModelsExpandDepth: -1,
deepLinking: true,
});
window.onload = () => {
loadSwaggerUI({
defaultModelsExpandDepth: -1,
deepLinking: true,
});
};
</script>
{% endblock %}

{#
Change Redocly configuration
All parameters are explained on Redocly website:
https://redocly.com/docs/redoc/config/
#}
{% block swagger_initialization %}
<script type="text/javascript">
window.onload = () => {
loadRedocly({
expandResponses: '200,201',
hideDownloadButton: true,
});
};
</script>
{% endblock %}

Expand Down
10 changes: 0 additions & 10 deletions phpstan-baseline.neon
Original file line number Diff line number Diff line change
@@ -1,15 +1,5 @@
parameters:
ignoreErrors:
-
message: "#^Property Nelmio\\\\ApiDocBundle\\\\Annotation\\\\Model\\:\\:\\$_required type has no value type specified in iterable type array\\.$#"
count: 1
path: src/Annotation/Model.php

-
message: "#^Property Nelmio\\\\ApiDocBundle\\\\Annotation\\\\Security\\:\\:\\$_required type has no value type specified in iterable type array\\.$#"
count: 1
path: src/Annotation/Security.php

-
message: "#^Only booleans are allowed in an if condition, mixed given\\.$#"
count: 1
Expand Down
6 changes: 3 additions & 3 deletions public/init-redocly-ui.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
'use strict';

window.onload = () => {
function loadRedocly(userOptions = {}) {
const data = JSON.parse(document.getElementById('swagger-data').innerText);

Redoc.init(data.spec, {}, document.getElementById('swagger-ui'));
};
Redoc.init(data.spec, userOptions, document.getElementById('swagger-ui'));
}
6 changes: 5 additions & 1 deletion src/Command/DumpCommand.php
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,9 @@
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Output\OutputInterface;

/**
* @final
*/
class DumpCommand extends Command
{
private RenderOpenApi $renderOpenApi;
Expand All @@ -28,6 +31,7 @@ class DumpCommand extends Command
private $defaultHtmlConfig = [
'assets_mode' => AssetsMode::CDN,
'swagger_ui_config' => [],
'redocly_config' => [],
];

public function __construct(RenderOpenApi $renderOpenApi)
Expand Down Expand Up @@ -67,7 +71,7 @@ protected function execute(InputInterface $input, OutputInterface $output): int
$options = [];
if (RenderOpenApi::HTML === $format) {
$rawHtmlConfig = json_decode($input->getOption('html-config'), true);
$options = is_array($rawHtmlConfig) ? $rawHtmlConfig : $this->defaultHtmlConfig;
$options = is_array($rawHtmlConfig) ? $rawHtmlConfig + $this->defaultHtmlConfig : $this->defaultHtmlConfig;
} elseif (RenderOpenApi::JSON === $format) {
$options = [
'no-pretty' => $input->hasParameterOption(['--no-pretty']),
Expand Down
24 changes: 24 additions & 0 deletions src/DependencyInjection/Configuration.php
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@

namespace Nelmio\ApiDocBundle\DependencyInjection;

use Nelmio\ApiDocBundle\Render\Html\AssetsMode;
use Symfony\Component\Config\Definition\Builder\TreeBuilder;
use Symfony\Component\Config\Definition\ConfigurationInterface;

Expand Down Expand Up @@ -55,6 +56,29 @@ public function getConfigTreeBuilder(): TreeBuilder
->defaultValue(['json'])
->prototype('scalar')->end()
->end()
->arrayNode('html_config')
->info('UI configuration options')
->addDefaultsIfNotSet()
->children()
->scalarNode('assets_mode')
->defaultValue(AssetsMode::CDN)
->validate()
->ifNotInArray([AssetsMode::BUNDLE, AssetsMode::CDN, AssetsMode::OFFLINE])
->thenInvalid('Invalid assets mode %s')
->end()
->end()
->arrayNode('swagger_ui_config')
->info('https://swagger.io/docs/open-source-tools/swagger-ui/usage/configuration/')
->addDefaultsIfNotSet()
->ignoreExtraKeys(false)
->end()
->arrayNode('redocly_config')
->info('https://redocly.com/docs/redoc/config/')
->addDefaultsIfNotSet()
->ignoreExtraKeys(false)
->end()
->end()
->end()
->arrayNode('areas')
->info('Filter the routes that are documented')
->defaultValue(
Expand Down
2 changes: 2 additions & 0 deletions src/DependencyInjection/NelmioApiDocExtension.php
Original file line number Diff line number Diff line change
Expand Up @@ -227,6 +227,8 @@ public function load(array $configs, ContainerBuilder $container): void

$container->removeDefinition('nelmio_api_doc.render_docs.html');
$container->removeDefinition('nelmio_api_doc.render_docs.html.asset');
} elseif (isset($config['html_config'])) {
$container->getDefinition('nelmio_api_doc.render_docs.html')->replaceArgument(1, $config['html_config']);
}

// ApiPlatform support
Expand Down
12 changes: 7 additions & 5 deletions src/Render/Html/HtmlOpenApiRenderer.php
Original file line number Diff line number Diff line change
Expand Up @@ -23,16 +23,20 @@ class HtmlOpenApiRenderer implements OpenApiRenderer
{
/** @var Environment|\Twig_Environment */
private $twig;
/** @var array<string, mixed> */
private array $htmlConfig;

/**
* @param Environment|\Twig_Environment $twig
* @param array<string, mixed> $htmlConfig
*/
public function __construct($twig)
public function __construct($twig, array $htmlConfig)
{
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->htmlConfig = $htmlConfig;
}

public function getFormat(): string
Expand All @@ -42,17 +46,15 @@ public function getFormat(): string

public function render(OpenApi $spec, array $options = []): string
{
$options += [
'assets_mode' => AssetsMode::CDN,
'swagger_ui_config' => [],
];
$options += $this->htmlConfig;

if (isset($options['ui_renderer']) && Renderer::REDOCLY === $options['ui_renderer']) {
return $this->twig->render(
'@NelmioApiDoc/Redocly/index.html.twig',
[
'swagger_data' => ['spec' => json_decode($spec->toJson(), true)],
'assets_mode' => $options['assets_mode'],
'redocly_config' => $options['redocly_config'],
]
);
}
Expand Down
11 changes: 10 additions & 1 deletion templates/Redocly/index.html.twig
Original file line number Diff line number Diff line change
Expand Up @@ -35,4 +35,13 @@
{% endblock javascripts %}

{{ nelmioAsset(assets_mode, 'init-redocly-ui.js') }}
</html>

{% block swagger_initialization %}
<script type="text/javascript">
window.onload = () => {
loadRedocly({{ redocly_config|json_encode(81)|raw }});
};
</script>
{% endblock swagger_initialization %}
DjordyKoert marked this conversation as resolved.
Show resolved Hide resolved
</body>
</html>
7 changes: 3 additions & 4 deletions templates/SwaggerUi/index.html.twig
Original file line number Diff line number Diff line change
Expand Up @@ -75,10 +75,9 @@ file that was distributed with this source code. #}

{% block swagger_initialization %}
<script type="text/javascript">
(function () {
var swaggerUI = {{ swagger_ui_config|json_encode(65)|raw }};
window.onload = loadSwaggerUI(swaggerUI);
})();
window.onload = () => {
loadSwaggerUI({{ swagger_ui_config|json_encode(65)|raw }});
};
DjordyKoert marked this conversation as resolved.
Show resolved Hide resolved
</script>
{% endblock swagger_initialization %}
</body>
Expand Down
11 changes: 11 additions & 0 deletions tests/Command/DumpCommandTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
namespace Nelmio\ApiDocBundle\Tests\Command;

use Nelmio\ApiDocBundle\Render\Html\AssetsMode;
use Nelmio\ApiDocBundle\Render\Html\Renderer;
use Nelmio\ApiDocBundle\Tests\Functional\WebTestCase; // for the creation of the kernel
use Symfony\Bundle\FrameworkBundle\Console\Application;
use Symfony\Component\Console\Tester\CommandTester;
Expand Down Expand Up @@ -102,6 +103,16 @@ public static function provideAssetsMode(): \Generator
'"supportedSubmitMethods":["get"]',
];

yield 'configure redocly' => [
[
'ui_renderer' => Renderer::REDOCLY,
'redocly_config' => [
'hideDownloadButton' => true,
],
],
'"hideDownloadButton":true',
];

yield 'configure server url' => [
[
'server_url' => 'http://example.com/api',
Expand Down
87 changes: 81 additions & 6 deletions tests/DependencyInjection/ConfigurationTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -13,14 +13,23 @@

use Nelmio\ApiDocBundle\DependencyInjection\Configuration;
use PHPUnit\Framework\TestCase;
use Symfony\Component\Config\Definition\Exception\InvalidConfigurationException;
use Symfony\Component\Config\Definition\Processor;

class ConfigurationTest extends TestCase
{
private Processor $processor;

protected function setUp(): void
{
$this->processor = new Processor();

parent::setUp();
}

public function testDefaultArea(): void
{
$processor = new Processor();
$config = $processor->processConfiguration(new Configuration(), [['areas' => ['path_patterns' => ['/foo']]]]);
$config = $this->processor->processConfiguration(new Configuration(), [['areas' => ['path_patterns' => ['/foo']]]]);

self::assertSame(
[
Expand All @@ -39,8 +48,7 @@ public function testDefaultArea(): void

public function testAreas(): void
{
$processor = new Processor();
$config = $processor->processConfiguration(new Configuration(), [['areas' => $areas = [
$config = $this->processor->processConfiguration(new Configuration(), [['areas' => $areas = [
'default' => [
'path_patterns' => ['/foo'],
'host_patterns' => [],
Expand Down Expand Up @@ -72,8 +80,7 @@ public function testAreas(): void

public function testAlternativeNames(): void
{
$processor = new Processor();
$config = $processor->processConfiguration(new Configuration(), [[
$config = $this->processor->processConfiguration(new Configuration(), [[
'models' => [
'names' => [
[
Expand Down Expand Up @@ -148,4 +155,72 @@ public function testAlternativeNames(): void
],
], $config['models']['names']);
}

/**
* @dataProvider provideInvalidConfiguration
*
* @param mixed[] $configuration
*/
public function testInvalidConfiguration(array $configuration, string $expectedError): void
{
self::expectException(InvalidConfigurationException::class);
self::expectExceptionMessage($expectedError);

$this->processor->processConfiguration(new Configuration(), [$configuration]);
}

public static function provideInvalidConfiguration(): \Generator
{
yield 'invalid html_config.assets_mode' => [
[
'html_config' => [
'assets_mode' => 'invalid',
],
],
'Invalid assets mode "invalid"',
];

yield 'do not set cache.item_id' => [
[
'cache' => [
'pool' => null,
'item_id' => 'some-id',
],
],
'Can not set cache.item_id if cache.pool is null',
];

yield 'do not set cache.item_id, default pool' => [
[
'cache' => [
'item_id' => 'some-id',
],
],
'Can not set cache.item_id if cache.pool is null',
];

yield 'default area missing ' => [
[
'areas' => [
'some_not_default_area' => [],
],
],
'You must specify a `default` area under `nelmio_api_doc.areas`.',
];

yield 'invalid groups value for model ' => [
[
'models' => [
'names' => [
[
'alias' => 'Foo1',
'type' => 'App\Foo',
'groups' => 'invalid_string_value',
],
],
],
],
'Model groups must be either `null` or an array.',
];
}
}
Loading
Loading