mirror of
https://github.com/retailcrm/NelmioApiDocBundle.git
synced 2025-02-09 02:59:27 +03:00
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:
parent
92f3eb633b
commit
393a6c061e
@ -7,3 +7,5 @@ enabled:
|
|||||||
|
|
||||||
disabled:
|
disabled:
|
||||||
- unalign_equals
|
- unalign_equals
|
||||||
|
- braces
|
||||||
|
- property_separation
|
||||||
|
@ -32,11 +32,12 @@ final class ApiDocGenerator
|
|||||||
* @param DescriberInterface[]|iterable $describers
|
* @param DescriberInterface[]|iterable $describers
|
||||||
* @param ModelDescriberInterface[]|iterable $modelDescribers
|
* @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->describers = $describers;
|
||||||
$this->modelDescribers = $modelDescribers;
|
$this->modelDescribers = $modelDescribers;
|
||||||
$this->cacheItemPool = $cacheItemPool;
|
$this->cacheItemPool = $cacheItemPool;
|
||||||
|
$this->cacheItemId = $cacheItemId;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function generate(): Swagger
|
public function generate(): Swagger
|
||||||
@ -46,7 +47,7 @@ final class ApiDocGenerator
|
|||||||
}
|
}
|
||||||
|
|
||||||
if ($this->cacheItemPool) {
|
if ($this->cacheItemPool) {
|
||||||
$item = $this->cacheItemPool->getItem('swagger_doc');
|
$item = $this->cacheItemPool->getItem($this->cacheItemId ?? 'swagger_doc');
|
||||||
if ($item->isHit()) {
|
if ($item->isHit()) {
|
||||||
return $this->swagger = $item->get();
|
return $this->swagger = $item->get();
|
||||||
}
|
}
|
||||||
|
15
CHANGELOG.md
15
CHANGELOG.md
@ -15,6 +15,21 @@ JMS Serializer
|
|||||||
SwaggerPHP
|
SwaggerPHP
|
||||||
* Handle `enum` and `default` properties from SwaggerPHP annotation
|
* 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)
|
3.0.0 (2017-12-10)
|
||||||
------------------
|
------------------
|
||||||
|
|
||||||
|
@ -12,21 +12,42 @@
|
|||||||
namespace Nelmio\ApiDocBundle\Controller;
|
namespace Nelmio\ApiDocBundle\Controller;
|
||||||
|
|
||||||
use Nelmio\ApiDocBundle\ApiDocGenerator;
|
use Nelmio\ApiDocBundle\ApiDocGenerator;
|
||||||
|
use Psr\Container\ContainerInterface;
|
||||||
|
use Symfony\Component\DependencyInjection\ServiceLocator;
|
||||||
use Symfony\Component\HttpFoundation\JsonResponse;
|
use Symfony\Component\HttpFoundation\JsonResponse;
|
||||||
use Symfony\Component\HttpFoundation\Request;
|
use Symfony\Component\HttpFoundation\Request;
|
||||||
|
use Symfony\Component\HttpKernel\Exception\BadRequestHttpException;
|
||||||
|
|
||||||
final class DocumentationController
|
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__));
|
||||||
}
|
}
|
||||||
|
|
||||||
public function __invoke(Request $request)
|
@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, $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()) {
|
if ('' !== $request->getBaseUrl()) {
|
||||||
$spec['basePath'] = $request->getBaseUrl();
|
$spec['basePath'] = $request->getBaseUrl();
|
||||||
}
|
}
|
||||||
|
@ -12,24 +12,45 @@
|
|||||||
namespace Nelmio\ApiDocBundle\Controller;
|
namespace Nelmio\ApiDocBundle\Controller;
|
||||||
|
|
||||||
use Nelmio\ApiDocBundle\ApiDocGenerator;
|
use Nelmio\ApiDocBundle\ApiDocGenerator;
|
||||||
|
use Psr\Container\ContainerInterface;
|
||||||
|
use Symfony\Component\DependencyInjection\ServiceLocator;
|
||||||
use Symfony\Component\HttpFoundation\Request;
|
use Symfony\Component\HttpFoundation\Request;
|
||||||
use Symfony\Component\HttpFoundation\Response;
|
use Symfony\Component\HttpFoundation\Response;
|
||||||
|
use Symfony\Component\HttpKernel\Exception\BadRequestHttpException;
|
||||||
|
|
||||||
final class SwaggerUiController
|
final class SwaggerUiController
|
||||||
{
|
{
|
||||||
private $apiDocGenerator;
|
private $generatorLocator;
|
||||||
|
|
||||||
private $twig;
|
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;
|
$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()) {
|
if ('' !== $request->getBaseUrl()) {
|
||||||
$spec['basePath'] = $request->getBaseUrl();
|
$spec['basePath'] = $request->getBaseUrl();
|
||||||
}
|
}
|
||||||
|
@ -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);
|
|
||||||
}
|
|
||||||
}
|
|
@ -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);
|
|
||||||
}
|
|
||||||
}
|
|
@ -12,20 +12,22 @@
|
|||||||
namespace Nelmio\ApiDocBundle\DependencyInjection\Compiler;
|
namespace Nelmio\ApiDocBundle\DependencyInjection\Compiler;
|
||||||
|
|
||||||
use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
|
use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
|
||||||
use Symfony\Component\DependencyInjection\Compiler\PriorityTaggedServiceTrait;
|
|
||||||
use Symfony\Component\DependencyInjection\ContainerBuilder;
|
use Symfony\Component\DependencyInjection\ContainerBuilder;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @internal
|
* @internal
|
||||||
*/
|
*/
|
||||||
final class AddDescribersPass implements CompilerPassInterface
|
final class TagDescribersPass implements CompilerPassInterface
|
||||||
{
|
{
|
||||||
use PriorityTaggedServiceTrait;
|
|
||||||
|
|
||||||
public function process(ContainerBuilder $container)
|
public function process(ContainerBuilder $container)
|
||||||
{
|
{
|
||||||
$describers = $this->findAndSortTaggedServices('nelmio_api_doc.describer', $container);
|
foreach ($container->findTaggedServiceIds('nelmio_api_doc.describer') as $id => $tags) {
|
||||||
|
$describer = $container->getDefinition($id);
|
||||||
$container->getDefinition('nelmio_api_doc.generator')->replaceArgument(0, $describers);
|
foreach ($container->getParameter('nelmio_api_doc.areas') as $area) {
|
||||||
|
foreach ($tags as $tag) {
|
||||||
|
$describer->addTag(sprintf('nelmio_api_doc.describer.%s', $area), $tag);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -21,6 +21,24 @@ final class Configuration implements ConfigurationInterface
|
|||||||
$treeBuilder = new TreeBuilder();
|
$treeBuilder = new TreeBuilder();
|
||||||
$treeBuilder
|
$treeBuilder
|
||||||
->root('nelmio_api_doc')
|
->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()
|
->children()
|
||||||
->arrayNode('documentation')
|
->arrayNode('documentation')
|
||||||
->useAttributeAsKey('key')
|
->useAttributeAsKey('key')
|
||||||
@ -28,8 +46,24 @@ final class Configuration implements ConfigurationInterface
|
|||||||
->example(['info' => ['title' => 'My App']])
|
->example(['info' => ['title' => 'My App']])
|
||||||
->prototype('variable')->end()
|
->prototype('variable')->end()
|
||||||
->end()
|
->end()
|
||||||
->arrayNode('routes')
|
->arrayNode('areas')
|
||||||
->info('Filter the routes that are documented')
|
->info('Filter the routes that are documented')
|
||||||
|
->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()
|
->addDefaultsIfNotSet()
|
||||||
->children()
|
->children()
|
||||||
->arrayNode('path_patterns')
|
->arrayNode('path_patterns')
|
||||||
@ -38,6 +72,7 @@ final class Configuration implements ConfigurationInterface
|
|||||||
->end()
|
->end()
|
||||||
->end()
|
->end()
|
||||||
->end()
|
->end()
|
||||||
|
->end()
|
||||||
->arrayNode('models')
|
->arrayNode('models')
|
||||||
->addDefaultsIfNotSet()
|
->addDefaultsIfNotSet()
|
||||||
->children()
|
->children()
|
||||||
|
@ -12,10 +12,14 @@
|
|||||||
namespace Nelmio\ApiDocBundle\DependencyInjection;
|
namespace Nelmio\ApiDocBundle\DependencyInjection;
|
||||||
|
|
||||||
use FOS\RestBundle\Controller\Annotations\ParamInterface;
|
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\ModelDescriber\JMSModelDescriber;
|
||||||
use Nelmio\ApiDocBundle\Routing\FilteredRouteCollectionBuilder;
|
use Nelmio\ApiDocBundle\Routing\FilteredRouteCollectionBuilder;
|
||||||
use phpDocumentor\Reflection\DocBlockFactory;
|
use phpDocumentor\Reflection\DocBlockFactory;
|
||||||
use Symfony\Component\Config\FileLocator;
|
use Symfony\Component\Config\FileLocator;
|
||||||
|
use Symfony\Component\DependencyInjection\Argument\TaggedIteratorArgument;
|
||||||
use Symfony\Component\DependencyInjection\ContainerBuilder;
|
use Symfony\Component\DependencyInjection\ContainerBuilder;
|
||||||
use Symfony\Component\DependencyInjection\Definition;
|
use Symfony\Component\DependencyInjection\Definition;
|
||||||
use Symfony\Component\DependencyInjection\Extension\PrependExtensionInterface;
|
use Symfony\Component\DependencyInjection\Extension\PrependExtensionInterface;
|
||||||
@ -54,22 +58,57 @@ final class NelmioApiDocExtension extends Extension implements PrependExtensionI
|
|||||||
$routesDefinition = (new Definition(RouteCollection::class))
|
$routesDefinition = (new Definition(RouteCollection::class))
|
||||||
->setFactory([new Reference('router'), 'getRouteCollection']);
|
->setFactory([new Reference('router'), 'getRouteCollection']);
|
||||||
|
|
||||||
if (0 === count($config['routes']['path_patterns'])) {
|
$container->setParameter('nelmio_api_doc.areas', array_keys($config['areas']));
|
||||||
$container->setDefinition('nelmio_api_doc.routes', $routesDefinition)
|
foreach ($config['areas'] as $area => $areaConfig) {
|
||||||
|
$container->register(sprintf('nelmio_api_doc.generator.%s', $area), ApiDocGenerator::class)
|
||||||
|
->setPublic(false)
|
||||||
|
->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);
|
->setPublic(false);
|
||||||
} else {
|
} else {
|
||||||
$container->register('nelmio_api_doc.routes', RouteCollection::class)
|
$container->register(sprintf('nelmio_api_doc.routes.%s', $area), RouteCollection::class)
|
||||||
->setPublic(false)
|
->setPublic(false)
|
||||||
->setFactory([
|
->setFactory([
|
||||||
(new Definition(FilteredRouteCollectionBuilder::class))
|
(new Definition(FilteredRouteCollectionBuilder::class))
|
||||||
->addArgument($config['routes']['path_patterns']),
|
->addArgument($areaConfig['path_patterns']),
|
||||||
'filter',
|
'filter',
|
||||||
])
|
])
|
||||||
->addArgument($routesDefinition);
|
->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'),
|
||||||
|
])
|
||||||
|
->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
|
// Import services needed for each library
|
||||||
$loader->load('swagger_php.xml');
|
|
||||||
if (class_exists(DocBlockFactory::class)) {
|
if (class_exists(DocBlockFactory::class)) {
|
||||||
$loader->load('php_doc.xml');
|
$loader->load('php_doc.xml');
|
||||||
}
|
}
|
||||||
|
@ -11,10 +11,8 @@
|
|||||||
|
|
||||||
namespace Nelmio\ApiDocBundle;
|
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\ConfigurationPass;
|
||||||
|
use Nelmio\ApiDocBundle\DependencyInjection\Compiler\TagDescribersPass;
|
||||||
use Symfony\Component\DependencyInjection\ContainerBuilder;
|
use Symfony\Component\DependencyInjection\ContainerBuilder;
|
||||||
use Symfony\Component\HttpKernel\Bundle\Bundle;
|
use Symfony\Component\HttpKernel\Bundle\Bundle;
|
||||||
|
|
||||||
@ -26,8 +24,6 @@ final class NelmioApiDocBundle extends Bundle
|
|||||||
public function build(ContainerBuilder $container)
|
public function build(ContainerBuilder $container)
|
||||||
{
|
{
|
||||||
$container->addCompilerPass(new ConfigurationPass());
|
$container->addCompilerPass(new ConfigurationPass());
|
||||||
$container->addCompilerPass(new AddDescribersPass());
|
$container->addCompilerPass(new TagDescribersPass());
|
||||||
$container->addCompilerPass(new AddModelDescribersPass());
|
|
||||||
$container->addCompilerPass(new AddRouteDescribersPass());
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -6,19 +6,16 @@
|
|||||||
<services>
|
<services>
|
||||||
<!-- Controllers -->
|
<!-- Controllers -->
|
||||||
<service id="nelmio_api_doc.controller.swagger_ui" class="Nelmio\ApiDocBundle\Controller\SwaggerUiController" public="true">
|
<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" />
|
<argument type="service" id="twig" />
|
||||||
</service>
|
</service>
|
||||||
|
|
||||||
<service id="nelmio_api_doc.controller.swagger" class="Nelmio\ApiDocBundle\Controller\DocumentationController" public="true">
|
<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>
|
</service>
|
||||||
|
|
||||||
<!-- Swagger Spec Generator -->
|
<!-- Swagger Spec Generator -->
|
||||||
<service id="nelmio_api_doc.generator" class="Nelmio\ApiDocBundle\ApiDocGenerator" public="true">
|
<service id="nelmio_api_doc.generator" alias="nelmio_api_doc.generator.default" />
|
||||||
<argument type="collection" /> <!-- Describers -->
|
|
||||||
<argument type="collection" /> <!-- Model Describers -->
|
|
||||||
</service>
|
|
||||||
|
|
||||||
<service id="nelmio_api_doc.controller_reflector" class="Nelmio\ApiDocBundle\Util\ControllerReflector" public="false">
|
<service id="nelmio_api_doc.controller_reflector" class="Nelmio\ApiDocBundle\Util\ControllerReflector" public="false">
|
||||||
<argument type="service" id="service_container" />
|
<argument type="service" id="service_container" />
|
||||||
@ -32,14 +29,6 @@
|
|||||||
<tag name="nelmio_api_doc.describer" priority="1000" />
|
<tag name="nelmio_api_doc.describer" priority="1000" />
|
||||||
</service>
|
</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">
|
<service id="nelmio_api_doc.describers.default" class="Nelmio\ApiDocBundle\Describer\DefaultDescriber" public="false">
|
||||||
<tag name="nelmio_api_doc.describer" priority="-1000" />
|
<tag name="nelmio_api_doc.describer" priority="-1000" />
|
||||||
</service>
|
</service>
|
||||||
|
@ -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
48
Resources/doc/areas.rst
Normal 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``.
|
@ -240,3 +240,13 @@ support in your config:
|
|||||||
|
|
||||||
nelmio_api_doc:
|
nelmio_api_doc:
|
||||||
models: { use_jms: false }
|
models: { use_jms: false }
|
||||||
|
|
||||||
|
Learn more
|
||||||
|
----------
|
||||||
|
|
||||||
|
If you need more complex features, take a look at:
|
||||||
|
|
||||||
|
.. toctree::
|
||||||
|
:maxdepth: 1
|
||||||
|
|
||||||
|
areas
|
||||||
|
36
Tests/ApiDocGeneratorTest.php
Normal file
36
Tests/ApiDocGeneratorTest.php
Normal 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());
|
||||||
|
}
|
||||||
|
}
|
41
Tests/Controller/ControllersTest.php
Normal file
41
Tests/Controller/ControllersTest.php
Normal 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());
|
||||||
|
}
|
||||||
|
}
|
62
Tests/DependencyInjection/ConfigurationTest.php
Normal file
62
Tests/DependencyInjection/ConfigurationTest.php
Normal 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']);
|
||||||
|
}
|
||||||
|
}
|
32
Tests/Functional/Controller/TestController.php
Normal file
32
Tests/Functional/Controller/TestController.php
Normal 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()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
@ -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']);
|
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();
|
$client = self::createClient();
|
||||||
$crawler = $client->request('GET', '/app_dev.php/docs/');
|
$crawler = $client->request('GET', '/app_dev.php'.$url);
|
||||||
|
|
||||||
$response = $client->getResponse();
|
$response = $client->getResponse();
|
||||||
$this->assertEquals(200, $response->getStatusCode());
|
$this->assertEquals(200, $response->getStatusCode());
|
||||||
$this->assertEquals('text/html; charset=UTF-8', $response->headers->get('Content-Type'));
|
$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 = $this->getSwaggerDefinition()->toArray();
|
||||||
$expected['basePath'] = '/app_dev.php';
|
$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()
|
public function testJsonDocs()
|
||||||
|
@ -64,11 +64,11 @@ class TestKernel extends Kernel
|
|||||||
*/
|
*/
|
||||||
protected function configureRoutes(RouteCollectionBuilder $routes)
|
protected function configureRoutes(RouteCollectionBuilder $routes)
|
||||||
{
|
{
|
||||||
|
$routes->import(__DIR__.'/Controller/TestController.php', '/', 'annotation');
|
||||||
$routes->import(__DIR__.'/Controller/ApiController.php', '/', 'annotation');
|
$routes->import(__DIR__.'/Controller/ApiController.php', '/', 'annotation');
|
||||||
$routes->import(__DIR__.'/Controller/UndocumentedController.php', '/', 'annotation');
|
$routes->import(__DIR__.'/Controller/UndocumentedController.php', '/', 'annotation');
|
||||||
$routes->import('', '/api', 'api_platform');
|
$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');
|
$routes->add('/docs.json', 'nelmio_api_doc.controller.swagger');
|
||||||
|
|
||||||
if ($this->useJMS) {
|
if ($this->useJMS) {
|
||||||
@ -110,8 +110,9 @@ class TestKernel extends Kernel
|
|||||||
'title' => 'My Test App',
|
'title' => 'My Test App',
|
||||||
],
|
],
|
||||||
],
|
],
|
||||||
'routes' => [
|
'areas' => [
|
||||||
'path_patterns' => ['^/api(?!/admin)'],
|
'default' => ['path_patterns' => ['^/api(?!/admin)']],
|
||||||
|
'test' => ['path_patterns' => ['^/test']],
|
||||||
],
|
],
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user