Add areas support (#1169)

* Add areas support

* Document the Areas feature

* Allow to expose swagger area as JSON

* Fixes

* last fixes
This commit is contained in:
Guilhem N 2018-01-05 13:08:02 +01:00 committed by GitHub
parent 92f3eb633b
commit 393a6c061e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
21 changed files with 447 additions and 153 deletions

View File

@ -7,3 +7,5 @@ enabled:
disabled:
- unalign_equals
- braces
- property_separation

View File

@ -32,11 +32,12 @@ final class ApiDocGenerator
* @param DescriberInterface[]|iterable $describers
* @param ModelDescriberInterface[]|iterable $modelDescribers
*/
public function __construct($describers, $modelDescribers, CacheItemPoolInterface $cacheItemPool = null)
public function __construct($describers, $modelDescribers, CacheItemPoolInterface $cacheItemPool = null, string $cacheItemId = null)
{
$this->describers = $describers;
$this->modelDescribers = $modelDescribers;
$this->cacheItemPool = $cacheItemPool;
$this->cacheItemId = $cacheItemId;
}
public function generate(): Swagger
@ -46,7 +47,7 @@ final class ApiDocGenerator
}
if ($this->cacheItemPool) {
$item = $this->cacheItemPool->getItem('swagger_doc');
$item = $this->cacheItemPool->getItem($this->cacheItemId ?? 'swagger_doc');
if ($item->isHit()) {
return $this->swagger = $item->get();
}

View File

@ -15,6 +15,21 @@ JMS Serializer
SwaggerPHP
* Handle `enum` and `default` properties from SwaggerPHP annotation
Config
* `nelmio_api_doc.routes` has been replaced by `nelmio_api_doc.areas`. Please update your config accordingly.
Before:
```yml
nelmio_api_doc:
routes: [ path_patterns: [ /api ] ]
```
After:
```yml
nelmio_api_doc:
areas: [ path_patterns: [ /api ] ]
```
3.0.0 (2017-12-10)
------------------

View File

@ -12,21 +12,42 @@
namespace Nelmio\ApiDocBundle\Controller;
use Nelmio\ApiDocBundle\ApiDocGenerator;
use Psr\Container\ContainerInterface;
use Symfony\Component\DependencyInjection\ServiceLocator;
use Symfony\Component\HttpFoundation\JsonResponse;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpKernel\Exception\BadRequestHttpException;
final class DocumentationController
{
private $apiDocGenerator;
private $generatorLocator;
public function __construct(ApiDocGenerator $apiDocGenerator)
/**
* @param ContainerInterface $generatorLocator
*/
public function __construct($generatorLocator)
{
$this->apiDocGenerator = $apiDocGenerator;
if (!$generatorLocator instanceof ContainerInterface) {
if (!$generatorLocator instanceof ApiDocGenerator) {
throw new \InvalidArgumentException(sprintf('Providing an instance of "%s" to "%s" is not supported.', get_class($generatorLocator), __METHOD__));
}
@trigger_error(sprintf('Providing an instance of "%s" to "%s()" is deprecated since version 3.1. Provide it an instance of "%s" instead.', ApiDocGenerator::class, __METHOD__, ContainerInterface::class), E_USER_DEPRECATED);
$generatorLocator = new ServiceLocator(['default' => function () use ($generatorLocator): ApiDocGenerator {
return $generatorLocator;
}]);
}
$this->generatorLocator = $generatorLocator;
}
public function __invoke(Request $request)
public function __invoke(Request $request, $area = 'default')
{
$spec = $this->apiDocGenerator->generate()->toArray();
if (!$this->generatorLocator->has($area)) {
throw new BadRequestHttpException(sprintf('Area "%s" is not supported.', $area));
}
$spec = $this->generatorLocator->get($area)->generate()->toArray();
if ('' !== $request->getBaseUrl()) {
$spec['basePath'] = $request->getBaseUrl();
}

View File

@ -12,24 +12,45 @@
namespace Nelmio\ApiDocBundle\Controller;
use Nelmio\ApiDocBundle\ApiDocGenerator;
use Psr\Container\ContainerInterface;
use Symfony\Component\DependencyInjection\ServiceLocator;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpKernel\Exception\BadRequestHttpException;
final class SwaggerUiController
{
private $apiDocGenerator;
private $generatorLocator;
private $twig;
public function __construct(ApiDocGenerator $apiDocGenerator, \Twig_Environment $twig)
/**
* @param ContainerInterface $generatorLocator
*/
public function __construct($generatorLocator, \Twig_Environment $twig)
{
$this->apiDocGenerator = $apiDocGenerator;
if (!$generatorLocator instanceof ContainerInterface) {
if (!$generatorLocator instanceof ApiDocGenerator) {
throw new \InvalidArgumentException(sprintf('Providing an instance of "%s" to "%s" is not supported.', get_class($generatorLocator), __METHOD__));
}
@trigger_error(sprintf('Providing an instance of "%s" to "%s()" is deprecated since version 3.1. Provide it an instance of "%s" instead.', ApiDocGenerator::class, __METHOD__, ContainerInterface::class), E_USER_DEPRECATED);
$generatorLocator = new ServiceLocator(['default' => function () use ($generatorLocator): ApiDocGenerator {
return $generatorLocator;
}]);
}
$this->generatorLocator = $generatorLocator;
$this->twig = $twig;
}
public function __invoke(Request $request)
public function __invoke(Request $request, $area = 'default')
{
$spec = $this->apiDocGenerator->generate()->toArray();
if (!$this->generatorLocator->has($area)) {
throw new BadRequestHttpException(sprintf('Area "%s" is not supported.', $area));
}
$spec = $this->generatorLocator->get($area)->generate()->toArray();
if ('' !== $request->getBaseUrl()) {
$spec['basePath'] = $request->getBaseUrl();
}

View File

@ -1,31 +0,0 @@
<?php
/*
* This file is part of the NelmioApiDocBundle package.
*
* (c) Nelmio
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Nelmio\ApiDocBundle\DependencyInjection\Compiler;
use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
use Symfony\Component\DependencyInjection\Compiler\PriorityTaggedServiceTrait;
use Symfony\Component\DependencyInjection\ContainerBuilder;
/**
* @internal
*/
final class AddModelDescribersPass implements CompilerPassInterface
{
use PriorityTaggedServiceTrait;
public function process(ContainerBuilder $container)
{
$modelDescribers = $this->findAndSortTaggedServices('nelmio_api_doc.model_describer', $container);
$container->getDefinition('nelmio_api_doc.generator')->replaceArgument(1, $modelDescribers);
}
}

View File

@ -1,31 +0,0 @@
<?php
/*
* This file is part of the NelmioApiDocBundle package.
*
* (c) Nelmio
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Nelmio\ApiDocBundle\DependencyInjection\Compiler;
use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
use Symfony\Component\DependencyInjection\Compiler\PriorityTaggedServiceTrait;
use Symfony\Component\DependencyInjection\ContainerBuilder;
/**
* @internal
*/
final class AddRouteDescribersPass implements CompilerPassInterface
{
use PriorityTaggedServiceTrait;
public function process(ContainerBuilder $container)
{
$routeDescribers = $this->findAndSortTaggedServices('nelmio_api_doc.route_describer', $container);
$container->getDefinition('nelmio_api_doc.describers.route')->replaceArgument(2, $routeDescribers);
}
}

View File

@ -12,20 +12,22 @@
namespace Nelmio\ApiDocBundle\DependencyInjection\Compiler;
use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
use Symfony\Component\DependencyInjection\Compiler\PriorityTaggedServiceTrait;
use Symfony\Component\DependencyInjection\ContainerBuilder;
/**
* @internal
*/
final class AddDescribersPass implements CompilerPassInterface
final class TagDescribersPass implements CompilerPassInterface
{
use PriorityTaggedServiceTrait;
public function process(ContainerBuilder $container)
{
$describers = $this->findAndSortTaggedServices('nelmio_api_doc.describer', $container);
$container->getDefinition('nelmio_api_doc.generator')->replaceArgument(0, $describers);
foreach ($container->findTaggedServiceIds('nelmio_api_doc.describer') as $id => $tags) {
$describer = $container->getDefinition($id);
foreach ($container->getParameter('nelmio_api_doc.areas') as $area) {
foreach ($tags as $tag) {
$describer->addTag(sprintf('nelmio_api_doc.describer.%s', $area), $tag);
}
}
}
}
}

View File

@ -21,6 +21,24 @@ final class Configuration implements ConfigurationInterface
$treeBuilder = new TreeBuilder();
$treeBuilder
->root('nelmio_api_doc')
->beforeNormalization()
->ifTrue(function ($v) {
return !isset($v['areas']) && isset($v['routes']);
})
->then(function ($v) {
$v['areas'] = $v['routes'];
unset($v['routes']);
@trigger_error('The `nelmio_api_doc.routes` config option is deprecated. Please use `nelmio_api_doc.areas` instead (just replace `routes` by `areas` in your config).', E_USER_DEPRECATED);
return $v;
})
->end()
->beforeNormalization()
->ifTrue(function ($v) {
return isset($v['routes']);
})
->thenInvalid('You must not use both `nelmio_api_doc.areas` and `nelmio_api_doc.routes` config options. Please update your config to only use `nelmio_api_doc.areas`.')
->end()
->children()
->arrayNode('documentation')
->useAttributeAsKey('key')
@ -28,13 +46,30 @@ final class Configuration implements ConfigurationInterface
->example(['info' => ['title' => 'My App']])
->prototype('variable')->end()
->end()
->arrayNode('routes')
->arrayNode('areas')
->info('Filter the routes that are documented')
->addDefaultsIfNotSet()
->children()
->arrayNode('path_patterns')
->example(['^/api', '^/api(?!/admin)'])
->prototype('scalar')->end()
->beforeNormalization()
->ifTrue(function ($v) {
return empty($v) or isset($v['path_patterns']);
})
->then(function ($v) {
return ['default' => $v];
})
->end()
->validate()
->ifTrue(function ($v) {
return !isset($v['default']);
})
->thenInvalid('You must specify a `default` area under `nelmio_api_doc.areas`.')
->end()
->useAttributeAsKey('name')
->prototype('array')
->addDefaultsIfNotSet()
->children()
->arrayNode('path_patterns')
->example(['^/api', '^/api(?!/admin)'])
->prototype('scalar')->end()
->end()
->end()
->end()
->end()

View File

@ -12,10 +12,14 @@
namespace Nelmio\ApiDocBundle\DependencyInjection;
use FOS\RestBundle\Controller\Annotations\ParamInterface;
use Nelmio\ApiDocBundle\ApiDocGenerator;
use Nelmio\ApiDocBundle\Describer\RouteDescriber;
use Nelmio\ApiDocBundle\Describer\SwaggerPhpDescriber;
use Nelmio\ApiDocBundle\ModelDescriber\JMSModelDescriber;
use Nelmio\ApiDocBundle\Routing\FilteredRouteCollectionBuilder;
use phpDocumentor\Reflection\DocBlockFactory;
use Symfony\Component\Config\FileLocator;
use Symfony\Component\DependencyInjection\Argument\TaggedIteratorArgument;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\Definition;
use Symfony\Component\DependencyInjection\Extension\PrependExtensionInterface;
@ -54,22 +58,57 @@ final class NelmioApiDocExtension extends Extension implements PrependExtensionI
$routesDefinition = (new Definition(RouteCollection::class))
->setFactory([new Reference('router'), 'getRouteCollection']);
if (0 === count($config['routes']['path_patterns'])) {
$container->setDefinition('nelmio_api_doc.routes', $routesDefinition)
->setPublic(false);
} else {
$container->register('nelmio_api_doc.routes', RouteCollection::class)
$container->setParameter('nelmio_api_doc.areas', array_keys($config['areas']));
foreach ($config['areas'] as $area => $areaConfig) {
$container->register(sprintf('nelmio_api_doc.generator.%s', $area), ApiDocGenerator::class)
->setPublic(false)
->setFactory([
(new Definition(FilteredRouteCollectionBuilder::class))
->addArgument($config['routes']['path_patterns']),
'filter',
->setArguments([
new TaggedIteratorArgument(sprintf('nelmio_api_doc.describer.%s', $area)),
new TaggedIteratorArgument('nelmio_api_doc.model_describer'),
]);
if (0 === count($areaConfig['path_patterns'])) {
$container->setDefinition(sprintf('nelmio_api_doc.routes.%s', $area), $routesDefinition)
->setPublic(false);
} else {
$container->register(sprintf('nelmio_api_doc.routes.%s', $area), RouteCollection::class)
->setPublic(false)
->setFactory([
(new Definition(FilteredRouteCollectionBuilder::class))
->addArgument($areaConfig['path_patterns']),
'filter',
])
->addArgument($routesDefinition);
}
$container->register(sprintf('nelmio_api_doc.describers.route.%s', $area), RouteDescriber::class)
->setPublic(false)
->setArguments([
new Reference(sprintf('nelmio_api_doc.routes.%s', $area)),
new Reference('nelmio_api_doc.controller_reflector'),
new TaggedIteratorArgument('nelmio_api_doc.route_describer'),
])
->addArgument($routesDefinition);
->addTag(sprintf('nelmio_api_doc.describer.%s', $area), ['priority' => -400]);
$container->register(sprintf('nelmio_api_doc.describers.swagger_php.%s', $area), SwaggerPhpDescriber::class)
->setPublic(false)
->setArguments([
new Reference(sprintf('nelmio_api_doc.routes.%s', $area)),
new Reference('nelmio_api_doc.controller_reflector'),
new Reference('annotation_reader'),
])
->addTag(sprintf('nelmio_api_doc.describer.%s', $area), ['priority' => -200]);
}
$container->register('nelmio_api_doc.generator_locator')
->setPublic(false)
->addTag('container.service_locator')
->addArgument(array_combine(
array_keys($config['areas']),
array_map(function ($area) { return new Reference(sprintf('nelmio_api_doc.generator.%s', $area)); }, array_keys($config['areas']))
));
// Import services needed for each library
$loader->load('swagger_php.xml');
if (class_exists(DocBlockFactory::class)) {
$loader->load('php_doc.xml');
}

View File

@ -11,10 +11,8 @@
namespace Nelmio\ApiDocBundle;
use Nelmio\ApiDocBundle\DependencyInjection\Compiler\AddDescribersPass;
use Nelmio\ApiDocBundle\DependencyInjection\Compiler\AddModelDescribersPass;
use Nelmio\ApiDocBundle\DependencyInjection\Compiler\AddRouteDescribersPass;
use Nelmio\ApiDocBundle\DependencyInjection\Compiler\ConfigurationPass;
use Nelmio\ApiDocBundle\DependencyInjection\Compiler\TagDescribersPass;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\HttpKernel\Bundle\Bundle;
@ -26,8 +24,6 @@ final class NelmioApiDocBundle extends Bundle
public function build(ContainerBuilder $container)
{
$container->addCompilerPass(new ConfigurationPass());
$container->addCompilerPass(new AddDescribersPass());
$container->addCompilerPass(new AddModelDescribersPass());
$container->addCompilerPass(new AddRouteDescribersPass());
$container->addCompilerPass(new TagDescribersPass());
}
}

View File

@ -6,19 +6,16 @@
<services>
<!-- Controllers -->
<service id="nelmio_api_doc.controller.swagger_ui" class="Nelmio\ApiDocBundle\Controller\SwaggerUiController" public="true">
<argument type="service" id="nelmio_api_doc.generator" />
<argument type="service" id="nelmio_api_doc.generator_locator" />
<argument type="service" id="twig" />
</service>
<service id="nelmio_api_doc.controller.swagger" class="Nelmio\ApiDocBundle\Controller\DocumentationController" public="true">
<argument type="service" id="nelmio_api_doc.generator" />
<argument type="service" id="nelmio_api_doc.generator_locator" />
</service>
<!-- Swagger Spec Generator -->
<service id="nelmio_api_doc.generator" class="Nelmio\ApiDocBundle\ApiDocGenerator" public="true">
<argument type="collection" /> <!-- Describers -->
<argument type="collection" /> <!-- Model Describers -->
</service>
<service id="nelmio_api_doc.generator" alias="nelmio_api_doc.generator.default" />
<service id="nelmio_api_doc.controller_reflector" class="Nelmio\ApiDocBundle\Util\ControllerReflector" public="false">
<argument type="service" id="service_container" />
@ -32,14 +29,6 @@
<tag name="nelmio_api_doc.describer" priority="1000" />
</service>
<service id="nelmio_api_doc.describers.route" class="Nelmio\ApiDocBundle\Describer\RouteDescriber" public="false">
<argument type="service" id="nelmio_api_doc.routes" />
<argument type="service" id="nelmio_api_doc.controller_reflector" />
<argument type="collection" />
<tag name="nelmio_api_doc.describer" priority="-400" />
</service>
<service id="nelmio_api_doc.describers.default" class="Nelmio\ApiDocBundle\Describer\DefaultDescriber" public="false">
<tag name="nelmio_api_doc.describer" priority="-1000" />
</service>

View File

@ -1,16 +0,0 @@
<?xml version="1.0" ?>
<container xmlns="http://symfony.com/schema/dic/services"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://symfony.com/schema/dic/services http://symfony.com/schema/dic/services/services-1.0.xsd">
<services>
<service id="nelmio_api_doc.describers.swagger_php" class="Nelmio\ApiDocBundle\Describer\SwaggerPhpDescriber" public="false">
<argument type="service" id="nelmio_api_doc.routes" />
<argument type="service" id="nelmio_api_doc.controller_reflector" />
<argument type="service" id="annotation_reader" />
<tag name="nelmio_api_doc.describer" priority="-200" />
</service>
</services>
</container>

48
Resources/doc/areas.rst Normal file
View File

@ -0,0 +1,48 @@
Areas
=====
We've already seen that you can configure which routes are documented using ``nelmio_api_doc.areas``:
.. code-block:: yaml
nelmio_api_doc:
areas:
path_patterns: [ ^/api ]
But in fact, this config option is way more powerful and allows you to split your documentation in several parts.
Configuration
-------------
You can define areas which will each generates a different documentation:
.. code-block:: yaml
nelmio_api_doc:
areas:
default:
path_patterns: [ ^/api ]
internal:
path_patterns: [ ^/internal ]
commercial:
path_patterns: [ ^/commercial ]
Your main documentation is under the ``default`` area. It's the one shown when accessing ``/api/doc``.
Then update your routing to be able to access your different documentations:
.. code-block:: yaml
# app/config/routing.yml
app.swagger_ui:
path: /api/doc/{area}
methods: GET
defaults: { _controller: nelmio_api_doc.controller.swagger_ui, area: default }
# To expose them as JSON
#app.swagger.areas:
# path: /api/doc/{area}.json
# methods: GET
# defaults: { _controller: nelmio_api_doc.controller.swagger }
That's all! You can now access ``/api/doc/internal`` and ``/api/doc/commercial``.

View File

@ -1,6 +1,6 @@
NelmioApiDocBundle
==================
The **NelmioApiDocBundle** bundle allows you to generate documentation in the
OpenAPI (Swagger) format and provides a sandbox to interactively browse the API documentation.
@ -12,7 +12,7 @@ This bundle supports _Symfony_ route requirements, PHP annotations,
[_FOSRestBundle_](https://github.com/FriendsOfSymfony/FOSRestBundle) annotations
and apps using [_Api-Platform_](https://github.com/api-platform/api-platform).
For models, it supports the Symfony serializer and the JMS serializer.
For models, it supports the Symfony serializer and the JMS serializer.
Migrate from 2.x to 3.0
-----------------------
@ -21,8 +21,8 @@ Migrate from 2.x to 3.0
Installation
------------
Open a command console, enter your project directory and execute the following command to download the latest version of this bundle:
Open a command console, enter your project directory and execute the following command to download the latest version of this bundle:
.. code-block:: bash
@ -65,7 +65,7 @@ Open a command console, enter your project directory and execute the following c
path: /api/doc.json
methods: GET
defaults: { _controller: nelmio_api_doc.controller.swagger }
As you just installed the bundle, you'll likely see routes you don't want in
your documentation such as `/_profiler/`. To fix this, you can filter the
routes that are documented by configuring the bundle:
@ -79,7 +79,7 @@ Open a command console, enter your project directory and execute the following c
- ^/api(?!/doc$)
How does this bundle work?
--------------------------
--------------------------
It generates you a swagger documentation from your symfony app thanks to
_Describers_. Each of these _Describers_ extract infos from various sources.
@ -88,11 +88,11 @@ routes, etc.
If you configured the ``app.swagger_ui`` route above, you can browse your
documentation at `http://example.org/api/doc`.
Using the bundle
----------------
You can configure global information in the bundle configuration ``documentation.info`` section (take a look at
You can configure global information in the bundle configuration ``documentation.info`` section (take a look at
[the Swagger specification](http://swagger.io/specification/) to know the fields
available):
@ -151,7 +151,7 @@ Use models
As shown in the example above, the bundle provides the ``@Model`` annotation.
When you use it, the bundle will deduce your model properties.
It has two options:
* ``type`` to specify your model's type::
@ -165,12 +165,12 @@ It has two options:
/**
* @Model(type=User::class, groups={"non_sensitive_data"})
*/
.. warning::
The ``@Model`` annotation acts like a ``@Schema`` annotation. If you nest it with a ``@Schema`` annotation, the bundle will consider that
you're documenting an array of models.
For instance, the following example::
/**
@ -215,7 +215,7 @@ It has two options:
public function myAction()
{
}
If you're not using the JMS Serializer
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@ -240,3 +240,13 @@ support in your config:
nelmio_api_doc:
models: { use_jms: false }
Learn more
----------
If you need more complex features, take a look at:
.. toctree::
:maxdepth: 1
areas

View File

@ -0,0 +1,36 @@
<?php
/*
* This file is part of the NelmioApiDocBundle package.
*
* (c) Nelmio
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Nelmio\ApiDocBundle\Tests;
use Nelmio\ApiDocBundle\ApiDocGenerator;
use Nelmio\ApiDocBundle\Describer\DefaultDescriber;
use PHPUnit\Framework\TestCase;
use Symfony\Component\Cache\Adapter\ArrayAdapter;
class ApiDocGeneratorTest extends TestCase
{
public function testCache()
{
$adapter = new ArrayAdapter();
$generator = new ApiDocGenerator([new DefaultDescriber()], [], $adapter);
$this->assertEquals($generator->generate(), $adapter->getItem('swagger_doc')->get());
}
public function testCacheWithCustomId()
{
$adapter = new ArrayAdapter();
$generator = new ApiDocGenerator([new DefaultDescriber()], [], $adapter, 'custom_id');
$this->assertEquals($generator->generate(), $adapter->getItem('custom_id')->get());
}
}

View File

@ -0,0 +1,41 @@
<?php
/*
* This file is part of the NelmioApiDocBundle package.
*
* (c) Nelmio
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Nelmio\ApiDocBundle\Tests\Controller;
use Nelmio\ApiDocBundle\ApiDocGenerator;
use Nelmio\ApiDocBundle\Controller\DocumentationController;
use Nelmio\ApiDocBundle\Controller\SwaggerUiController;
use PHPUnit\Framework\TestCase;
use Symfony\Component\HttpFoundation\Request;
class ControllersTest extends TestCase
{
/**
* @group legacy
* @expectedDeprecation Providing an instance of "Nelmio\ApiDocBundle\ApiDocGenerator" to "Nelmio\ApiDocBundle\Controller\SwaggerUiController::__construct()" is deprecated since version 3.1. Provide it an instance of "Psr\Container\ContainerInterface" instead.
*/
public function testSwaggerUiControllerInstanciation()
{
$controller = new SwaggerUiController(new ApiDocGenerator([], []), $this->createMock('Twig_Environment'));
$controller(new Request());
}
/**
* @group legacy
* @expectedDeprecation Providing an instance of "Nelmio\ApiDocBundle\ApiDocGenerator" to "Nelmio\ApiDocBundle\Controller\DocumentationController::__construct()" is deprecated since version 3.1. Provide it an instance of "Psr\Container\ContainerInterface" instead.
*/
public function testDocumentationControllerInstanciation()
{
$controller = new DocumentationController(new ApiDocGenerator([], []));
$controller(new Request());
}
}

View File

@ -0,0 +1,62 @@
<?php
/*
* This file is part of the NelmioApiDocBundle package.
*
* (c) Nelmio
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Nelmio\ApiDocBundle\Tests\DependencyInjection;
use Nelmio\ApiDocBundle\DependencyInjection\Configuration;
use PHPUnit\Framework\TestCase;
use Symfony\Component\Config\Definition\Processor;
class ConfigurationTest extends TestCase
{
public function testDefaultArea()
{
$processor = new Processor();
$config = $processor->processConfiguration(new Configuration(), [['areas' => ['path_patterns' => ['/foo']]]]);
$this->assertEquals(['default' => ['path_patterns' => ['/foo']]], $config['areas']);
}
public function testAreas()
{
$processor = new Processor();
$config = $processor->processConfiguration(new Configuration(), [['areas' => $areas = [
'default' => ['path_patterns' => ['/foo']],
'internal' => ['path_patterns' => ['/internal']],
'commercial' => ['path_patterns' => ['/internal']],
]]]);
$this->assertEquals($areas, $config['areas']);
}
/**
* @group legacy
* @expectedException \InvalidArgumentException
* @expectedExceptionMessage You must not use both `nelmio_api_doc.areas` and `nelmio_api_doc.routes` config options. Please update your config to only use `nelmio_api_doc.areas`.
*/
public function testBothAreasAndRoutes()
{
$processor = new Processor();
$config = $processor->processConfiguration(new Configuration(), [['areas' => [], 'routes' => []]]);
}
/**
* @group legacy
* @expectedDeprecation The `nelmio_api_doc.routes` config option is deprecated. Please use `nelmio_api_doc.areas` instead (just replace `routes` by `areas` in your config).
*/
public function testDefaultConfig()
{
$processor = new Processor();
$config = $processor->processConfiguration(new Configuration(), [['routes' => ['path_patterns' => ['/foo']]]]);
$this->assertEquals(['default' => ['path_patterns' => ['/foo']]], $config['areas']);
}
}

View File

@ -0,0 +1,32 @@
<?php
/*
* This file is part of the NelmioApiDocBundle package.
*
* (c) Nelmio
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Nelmio\ApiDocBundle\Tests\Functional\Controller;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route;
use Swagger\Annotations as SWG;
/**
* @Route("/test")
*/
class TestController
{
/**
* @SWG\Response(
* response="200",
* description="Test"
* )
* @Route("/test/", methods={"GET"})
*/
public function testAction()
{
}
}

View File

@ -18,19 +18,40 @@ class SwaggerUiTest extends WebTestCase
return parent::createClient([], ['PHP_SELF' => '/app_dev.php/docs', 'SCRIPT_FILENAME' => '/var/www/app/web/app_dev.php']);
}
public function testSwaggerUi()
/**
* @dataProvider areaProvider
*/
public function testSwaggerUi($url, $area, $expected)
{
$client = self::createClient();
$crawler = $client->request('GET', '/app_dev.php/docs/');
$crawler = $client->request('GET', '/app_dev.php'.$url);
$response = $client->getResponse();
$this->assertEquals(200, $response->getStatusCode());
$this->assertEquals('text/html; charset=UTF-8', $response->headers->get('Content-Type'));
$this->assertEquals($expected, json_decode($crawler->filterXPath('//script[@id="swagger-data"]')->text(), true)['spec']);
}
public function areaProvider()
{
$expected = $this->getSwaggerDefinition()->toArray();
$expected['basePath'] = '/app_dev.php';
$this->assertEquals($expected, json_decode($crawler->filterXPath('//script[@id="swagger-data"]')->text(), true)['spec']);
yield ['/docs', 'default', $expected];
// Api-platform documentation
$expected['paths'] = [
'/api/dummies' => $expected['paths']['/api/dummies'],
'/api/foo' => $expected['paths']['/api/foo'],
'/api/dummies/{id}' => $expected['paths']['/api/dummies/{id}'],
'/test/test/' => ['get' => [
'responses' => ['200' => ['description' => 'Test']],
]],
];
$expected['definitions'] = ['Dummy' => $expected['definitions']['Dummy']];
yield ['/docs/test', 'test', $expected];
}
public function testJsonDocs()

View File

@ -64,11 +64,11 @@ class TestKernel extends Kernel
*/
protected function configureRoutes(RouteCollectionBuilder $routes)
{
$routes->import(__DIR__.'/Controller/TestController.php', '/', 'annotation');
$routes->import(__DIR__.'/Controller/ApiController.php', '/', 'annotation');
$routes->import(__DIR__.'/Controller/UndocumentedController.php', '/', 'annotation');
$routes->import('', '/api', 'api_platform');
$routes->import('@NelmioApiDocBundle/Resources/config/routing/swaggerui.xml', '/docs');
$routes->add('/docs/{area}', 'nelmio_api_doc.controller.swagger_ui')->setDefault('area', 'default');
$routes->add('/docs.json', 'nelmio_api_doc.controller.swagger');
if ($this->useJMS) {
@ -110,8 +110,9 @@ class TestKernel extends Kernel
'title' => 'My Test App',
],
],
'routes' => [
'path_patterns' => ['^/api(?!/admin)'],
'areas' => [
'default' => ['path_patterns' => ['^/api(?!/admin)']],
'test' => ['path_patterns' => ['^/test']],
],
]);
}