From 3364bf3af33fb3f197b7db09ed3061e76fda8017 Mon Sep 17 00:00:00 2001 From: Guilhem N Date: Wed, 30 Nov 2016 14:08:10 +0100 Subject: [PATCH] Allow to filter routes based on their path --- DependencyInjection/Configuration.php | 16 ++++++- DependencyInjection/EXSystApiDocExtension.php | 4 ++ Describer/RouteDescriber.php | 22 +++------ Resources/config/services.xml | 16 ++++++- Routing/FilteredRouteCollectionBuilder.php | 48 +++++++++++++++++++ Tests/Functional/Controller/ApiController.php | 12 +++++ .../Controller/UndocumentedController.php | 27 +++++++++++ Tests/Functional/FunctionalTest.php | 13 +++-- Tests/Functional/TestKernel.php | 7 +++ 9 files changed, 144 insertions(+), 21 deletions(-) create mode 100644 Routing/FilteredRouteCollectionBuilder.php create mode 100644 Tests/Functional/Controller/UndocumentedController.php diff --git a/DependencyInjection/Configuration.php b/DependencyInjection/Configuration.php index 0b5d88e..ae2fc06 100644 --- a/DependencyInjection/Configuration.php +++ b/DependencyInjection/Configuration.php @@ -19,7 +19,21 @@ class Configuration implements ConfigurationInterface public function getConfigTreeBuilder() { $treeBuilder = new TreeBuilder(); - $rootNode = $treeBuilder->root('exsyst_api_doc'); + $treeBuilder + ->root('exsyst_api_doc') + ->children() + ->arrayNode('routes') + ->info('Filter the routes that are documented') + ->addDefaultsIfNotSet() + ->children() + ->arrayNode('path_patterns') + ->example(array('^/api', '^/api(?!/admin)')) + ->prototype('scalar')->end() + ->end() + ->end() + ->end() + ->end(); + return $treeBuilder; } diff --git a/DependencyInjection/EXSystApiDocExtension.php b/DependencyInjection/EXSystApiDocExtension.php index 87758eb..22f4eb4 100644 --- a/DependencyInjection/EXSystApiDocExtension.php +++ b/DependencyInjection/EXSystApiDocExtension.php @@ -40,6 +40,10 @@ class EXSystApiDocExtension extends Extension $loader->load('services.xml'); + // Filter routes + $routeCollectionBuilder = $container->getDefinition('exsyst_api_doc.describers.route.filtered_route_collection_builder'); + $routeCollectionBuilder->replaceArgument(0, $config['routes']['path_patterns']); + // Import services needed for each library if (class_exists(ApiDoc::class)) { $loader->load('nelmio_apidoc.xml'); diff --git a/Describer/RouteDescriber.php b/Describer/RouteDescriber.php index f8e884e..2e67430 100644 --- a/Describer/RouteDescriber.php +++ b/Describer/RouteDescriber.php @@ -17,25 +17,25 @@ use EXSyst\Component\Swagger\Swagger; use Symfony\Bundle\FrameworkBundle\Controller\ControllerNameParser; use Symfony\Component\DependencyInjection\ContainerInterface; use Symfony\Component\Routing\Route; -use Symfony\Component\Routing\RouterInterface; +use Symfony\Component\Routing\RouteCollection; class RouteDescriber implements DescriberInterface { private $container; - private $router; + private $routeCollection; private $controllerNameParser; private $routeDescribers; /** * @param ContainerInterface $container - * @param RouterInterface $router + * @param RouteCollection $routeCollection * @param ControllerNameParser $controllerNameParser * @param RouteDescriberInterface[] $routeDescribers */ - public function __construct(ContainerInterface $container, RouterInterface $router, ControllerNameParser $controllerNameParser, array $routeDescribers) + public function __construct(ContainerInterface $container, RouteCollection $routeCollection, ControllerNameParser $controllerNameParser, array $routeDescribers) { $this->container = $container; - $this->router = $router; + $this->routeCollection = $routeCollection; $this->controllerNameParser = $controllerNameParser; $this->routeDescribers = $routeDescribers; } @@ -46,7 +46,7 @@ class RouteDescriber implements DescriberInterface return; } - foreach ($this->getRoutes() as $route) { + foreach ($this->routeCollection->all() as $route) { // if able to resolve the controller if ($method = $this->getReflectionMethod($route->getDefault('_controller'))) { // Extract as many informations as possible about this route @@ -57,16 +57,6 @@ class RouteDescriber implements DescriberInterface } } - /** - * Return a list of route to inspect. - * - * @return Route[] An array of routes - */ - private function getRoutes() - { - return $this->router->getRouteCollection()->all(); - } - /** * Returns the ReflectionMethod for the given controller string. * diff --git a/Resources/config/services.xml b/Resources/config/services.xml index c352ee3..378ff9f 100644 --- a/Resources/config/services.xml +++ b/Resources/config/services.xml @@ -9,9 +9,23 @@ + + + + + - + + + + + + + + + + diff --git a/Routing/FilteredRouteCollectionBuilder.php b/Routing/FilteredRouteCollectionBuilder.php new file mode 100644 index 0000000..835d750 --- /dev/null +++ b/Routing/FilteredRouteCollectionBuilder.php @@ -0,0 +1,48 @@ +pathPatterns = $pathPatterns; + } + + public function filter(RouteCollection $routes): RouteCollection + { + $filteredRoutes = new RouteCollection(); + foreach ($routes->all() as $name => $route) { + if ($this->match($route)) { + $filteredRoutes->add($name, $route); + } + } + + return $filteredRoutes; + } + + private function match(Route $route): bool + { + foreach ($this->pathPatterns as $pathPattern) { + if (!preg_match('{'.$pathPattern.'}', $route->getPath())) { + return false; + } + } + + return true; + } +} diff --git a/Tests/Functional/Controller/ApiController.php b/Tests/Functional/Controller/ApiController.php index 8fd84d8..4c50fc2 100644 --- a/Tests/Functional/Controller/ApiController.php +++ b/Tests/Functional/Controller/ApiController.php @@ -14,6 +14,9 @@ namespace EXSyst\Bundle\ApiDocBundle\Tests\Functional\Controller; use Nelmio\ApiDocBundle\Annotation\ApiDoc; use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route; +/** + * @Route("/api") + */ class ApiController { /** @@ -45,4 +48,13 @@ class ApiController public function deprecatedAction() { } + + /** + * This action is not documented. It is excluded by the config. + * + * @Route("/admin", methods={"GET"}) + */ + public function adminAction() + { + } } diff --git a/Tests/Functional/Controller/UndocumentedController.php b/Tests/Functional/Controller/UndocumentedController.php new file mode 100644 index 0000000..e0a2c5d --- /dev/null +++ b/Tests/Functional/Controller/UndocumentedController.php @@ -0,0 +1,27 @@ +getSwaggerDefinition()->getPaths(); + $this->assertFalse($paths->has('/undocumented')); + $this->assertFalse($paths->has('/api/admin')); + } + public function testUserAction() { - $operation = $this->getOperation('/test/{user}', 'get'); + $operation = $this->getOperation('/api/test/{user}', 'get'); $this->assertEquals(['https'], $operation->getSchemes()); $this->assertEmpty($operation->getSummary()); @@ -35,7 +42,7 @@ class FunctionalTest extends WebTestCase public function testNelmioAction() { - $operation = $this->getOperation('/nelmio/{foo}', 'post'); + $operation = $this->getOperation('/api/nelmio/{foo}', 'post'); $this->assertEquals('This action is described.', $operation->getDescription()); $this->assertNull($operation->getDeprecated()); @@ -47,7 +54,7 @@ class FunctionalTest extends WebTestCase public function testDeprecatedAction() { - $operation = $this->getOperation('/deprecated', 'get'); + $operation = $this->getOperation('/api/deprecated', 'get'); $this->assertEquals('This action is deprecated.', $operation->getSummary()); $this->assertEquals('Please do not use this action.', $operation->getDescription()); diff --git a/Tests/Functional/TestKernel.php b/Tests/Functional/TestKernel.php index 7b7b110..621becd 100644 --- a/Tests/Functional/TestKernel.php +++ b/Tests/Functional/TestKernel.php @@ -59,5 +59,12 @@ class TestKernel extends Kernel 'test' => null, 'validation' => null, ]); + + // Filter routes + $c->loadFromExtension('exsyst_api_doc', [ + 'routes' => [ + 'path_patterns' => ['^/api(?!/admin)'], + ], + ]); } }