From 30b170479ca27dd7c79ba82e71a34148e0ab5cde Mon Sep 17 00:00:00 2001 From: Frederik Holz Date: Tue, 31 Aug 2021 16:14:32 +0200 Subject: [PATCH 1/7] Add new config node disable_default_routes to prevent registering RouteDescriber to specific areas which allowes disabling rendering of default routes, Adjust tests accordingly --- DependencyInjection/Configuration.php | 5 +++++ DependencyInjection/NelmioApiDocExtension.php | 18 ++++++++++-------- Routing/FilteredRouteCollectionBuilder.php | 2 ++ .../DependencyInjection/ConfigurationTest.php | 5 +++++ 4 files changed, 22 insertions(+), 8 deletions(-) diff --git a/DependencyInjection/Configuration.php b/DependencyInjection/Configuration.php index affdbb6a0..7fcbb656a 100644 --- a/DependencyInjection/Configuration.php +++ b/DependencyInjection/Configuration.php @@ -63,6 +63,7 @@ public function getConfigTreeBuilder() 'with_annotation' => false, 'documentation' => [], 'name_patterns' => [], + 'disable_default_routes' => false, ], ] ) @@ -103,6 +104,10 @@ public function getConfigTreeBuilder() ->defaultFalse() ->info('whether to filter by annotation') ->end() + ->booleanNode('disable_default_routes') + ->defaultFalse() + ->info('if set disables default routes without annotations') + ->end() ->arrayNode('documentation') ->useAttributeAsKey('key') ->defaultValue([]) diff --git a/DependencyInjection/NelmioApiDocExtension.php b/DependencyInjection/NelmioApiDocExtension.php index 8e2669bfb..62a0e42af 100644 --- a/DependencyInjection/NelmioApiDocExtension.php +++ b/DependencyInjection/NelmioApiDocExtension.php @@ -74,14 +74,16 @@ public function load(array $configs, ContainerBuilder $container) new TaggedIteratorArgument('nelmio_api_doc.model_describer'), ]); - $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]); + if (!array_key_exists('disable_default_routes', $areaConfig) || true !== $areaConfig['disable_default_routes']) { + $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) diff --git a/Routing/FilteredRouteCollectionBuilder.php b/Routing/FilteredRouteCollectionBuilder.php index 7e989eeda..773e3b711 100644 --- a/Routing/FilteredRouteCollectionBuilder.php +++ b/Routing/FilteredRouteCollectionBuilder.php @@ -45,11 +45,13 @@ public function __construct( 'host_patterns' => [], 'name_patterns' => [], 'with_annotation' => false, + 'disable_default_routes' => false, ]) ->setAllowedTypes('path_patterns', 'string[]') ->setAllowedTypes('host_patterns', 'string[]') ->setAllowedTypes('name_patterns', 'string[]') ->setAllowedTypes('with_annotation', 'boolean') + ->setAllowedTypes('disable_default_routes', 'boolean') ; if (array_key_exists(0, $options)) { diff --git a/Tests/DependencyInjection/ConfigurationTest.php b/Tests/DependencyInjection/ConfigurationTest.php index 3a7d5458d..ec0bd01b2 100644 --- a/Tests/DependencyInjection/ConfigurationTest.php +++ b/Tests/DependencyInjection/ConfigurationTest.php @@ -29,6 +29,7 @@ public function testDefaultArea() 'host_patterns' => [], 'name_patterns' => [], 'with_annotation' => false, + 'disable_default_routes' => false, 'documentation' => [], ], ], @@ -46,6 +47,7 @@ public function testAreas() 'with_annotation' => false, 'documentation' => [], 'name_patterns' => [], + 'disable_default_routes' => false, ], 'internal' => [ 'path_patterns' => ['/internal'], @@ -53,6 +55,7 @@ public function testAreas() 'with_annotation' => false, 'documentation' => [], 'name_patterns' => [], + 'disable_default_routes' => false, ], 'commercial' => [ 'path_patterns' => ['/internal'], @@ -60,6 +63,7 @@ public function testAreas() 'with_annotation' => false, 'documentation' => [], 'name_patterns' => [], + 'disable_default_routes' => false, ], ]]]); @@ -173,6 +177,7 @@ public function testDefaultConfig() 'host_patterns' => [], 'name_patterns' => [], 'with_annotation' => false, + 'disable_default_routes' => false, 'documentation' => [], ], ], From 2bb3636d9f7fb3e26de3f1ec64cbfdfa65216bce Mon Sep 17 00:00:00 2001 From: Frederik Holz Date: Tue, 31 Aug 2021 16:38:05 +0200 Subject: [PATCH 2/7] Add documentation for new config variable --- Resources/doc/faq.rst | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/Resources/doc/faq.rst b/Resources/doc/faq.rst index bf1d0c6b5..f3a9122e1 100644 --- a/Resources/doc/faq.rst +++ b/Resources/doc/faq.rst @@ -197,3 +197,17 @@ A: Use ``@SWG\Tag`` annotation. { //... } + +Disable Default Section +----------------------- + +Q: I don't want to render the "default" section, how do I do that? + +A: Use ``disable_default_routes`` config in your area. + +.. code-block:: yaml + + nelmio_api_doc: + areas: + default: + disable_default_routes: true From dad6071b931ef8d0195a0dd5bc4ddc50d56b0a9f Mon Sep 17 00:00:00 2001 From: Frederik Holz Date: Mon, 6 Sep 2021 13:27:58 +0200 Subject: [PATCH 3/7] Change disable_default_routes to filter route collection instead of disabling route describer, Add test for new handling in FilteredRouteCollectionBuilder --- DependencyInjection/NelmioApiDocExtension.php | 19 +++-- Routing/FilteredRouteCollectionBuilder.php | 28 ++++++++ .../FilteredRouteCollectionBuilderTest.php | 69 +++++++++++++++++++ 3 files changed, 106 insertions(+), 10 deletions(-) diff --git a/DependencyInjection/NelmioApiDocExtension.php b/DependencyInjection/NelmioApiDocExtension.php index 62a0e42af..a7cb54530 100644 --- a/DependencyInjection/NelmioApiDocExtension.php +++ b/DependencyInjection/NelmioApiDocExtension.php @@ -74,16 +74,14 @@ public function load(array $configs, ContainerBuilder $container) new TaggedIteratorArgument('nelmio_api_doc.model_describer'), ]); - if (!array_key_exists('disable_default_routes', $areaConfig) || true !== $areaConfig['disable_default_routes']) { - $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.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) @@ -108,6 +106,7 @@ public function load(array $configs, ContainerBuilder $container) && 0 === count($areaConfig['host_patterns']) && 0 === count($areaConfig['name_patterns']) && false === $areaConfig['with_annotation'] + && false === $areaConfig['disable_default_routes'] ) { $container->setDefinition(sprintf('nelmio_api_doc.routes.%s', $area), $routesDefinition) ->setPublic(false); diff --git a/Routing/FilteredRouteCollectionBuilder.php b/Routing/FilteredRouteCollectionBuilder.php index 773e3b711..d172de937 100644 --- a/Routing/FilteredRouteCollectionBuilder.php +++ b/Routing/FilteredRouteCollectionBuilder.php @@ -75,6 +75,7 @@ public function filter(RouteCollection $routes): RouteCollection && $this->matchHost($route) && $this->matchAnnotation($route) && $this->matchName($name) + && $this->matchDefaultRoute($route) ) { $filteredRoutes->add($name, $route); } @@ -137,4 +138,31 @@ private function matchAnnotation(Route $route): bool return (null !== $areas) ? $areas->has($this->area) : false; } + + private function matchDefaultRoute(Route $route): bool + { + if (false === $this->options['disable_default_routes']) { + return true; + } + + $method = $this->controllerReflector->getReflectionMethod( + $route->getDefault('_controller') ?? '' + ); + + if (null === $method) { + return false; + } + + $annotations = $this->annotationReader->getMethodAnnotations($method); + + foreach ($annotations as $annotation) { + if (false !== strpos(get_class($annotation), 'Nelmio\\ApiDocBundle\\Annotation') + || false !== strpos(get_class($annotation), 'Swagger\\Annotations') + ) { + return true; + } + } + + return false; + } } diff --git a/Tests/Routing/FilteredRouteCollectionBuilderTest.php b/Tests/Routing/FilteredRouteCollectionBuilderTest.php index 744e5234e..0545d535f 100644 --- a/Tests/Routing/FilteredRouteCollectionBuilderTest.php +++ b/Tests/Routing/FilteredRouteCollectionBuilderTest.php @@ -14,9 +14,11 @@ use Doctrine\Common\Annotations\AnnotationReader; use Doctrine\Common\Annotations\Reader; use Nelmio\ApiDocBundle\Annotation\Areas; +use Nelmio\ApiDocBundle\Annotation\Operation; use Nelmio\ApiDocBundle\Routing\FilteredRouteCollectionBuilder; use Nelmio\ApiDocBundle\Util\ControllerReflector; use PHPUnit\Framework\TestCase; +use Swagger\Annotations\Parameter; use Symfony\Bundle\FrameworkBundle\Controller\ControllerNameParser; use Symfony\Component\DependencyInjection\Container; use Symfony\Component\Routing\Route; @@ -245,6 +247,73 @@ public function getNonMatchingRoutes(): array ]; } + /** + * @dataProvider getMatchingRoutesWithDisabledDefaultRoutes + * @param array $annotations + * @param array $options + */ + public function testRoutesWithDisabledDefaultRoutes( + string $name, + Route $route, + array $annotations, + array $options, + int $expectedRoutesCount + ): void { + $routes = new RouteCollection(); + $routes->add($name, $route); + $area = 'area'; + + $reflectionMethodStub = $this->createMock(\ReflectionMethod::class); + $controllerReflectorStub = $this->createMock(ControllerReflector::class); + $controllerReflectorStub->method('getReflectionMethod')->willReturn($reflectionMethodStub); + + $annotationReader = $this->createMock(Reader::class); + $annotationReader + ->method('getMethodAnnotations') + ->willReturn($annotations) + ; + + $routeBuilder = new FilteredRouteCollectionBuilder( + $annotationReader, + $controllerReflectorStub, + $area, + $options + ); + $filteredRoutes = $routeBuilder->filter($routes); + + $this->assertCount($expectedRoutesCount, $filteredRoutes); + } + + /** + * @return array + */ + public function getMatchingRoutesWithDisabledDefaultRoutes(): array + { + return [ + 'non matching route without Annotation' => [ + 'r10', + new Route('/api/foo', ['_controller' => 'ApiController::fooAction']), + [], + ['disable_default_routes' => true], + 0, + ], + 'matching route with Nelmio Annotation' => [ + 'r10', + new Route('/api/foo', ['_controller' => 'ApiController::fooAction']), + [new Operation([])], + ['disable_default_routes' => true], + 1, + ], + 'matching route with Swagger Annotation' => [ + 'r10', + new Route('/api/foo', ['_controller' => 'ApiController::fooAction']), + [new Parameter([])], + ['disable_default_routes' => true], + 1, + ], + ]; + } + private function createControllerReflector(): ControllerReflector { if (class_exists(ControllerNameParser::class)) { From 89fcfd81647a16d68f432175b3af6eb81bd3bbd6 Mon Sep 17 00:00:00 2001 From: Frederik Holz Date: Mon, 6 Sep 2021 13:32:31 +0200 Subject: [PATCH 4/7] Change naming of matching method for disabling default routes --- Routing/FilteredRouteCollectionBuilder.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Routing/FilteredRouteCollectionBuilder.php b/Routing/FilteredRouteCollectionBuilder.php index d172de937..97294944b 100644 --- a/Routing/FilteredRouteCollectionBuilder.php +++ b/Routing/FilteredRouteCollectionBuilder.php @@ -75,7 +75,7 @@ public function filter(RouteCollection $routes): RouteCollection && $this->matchHost($route) && $this->matchAnnotation($route) && $this->matchName($name) - && $this->matchDefaultRoute($route) + && $this->defaultRouteDisabled($route) ) { $filteredRoutes->add($name, $route); } @@ -139,7 +139,7 @@ private function matchAnnotation(Route $route): bool return (null !== $areas) ? $areas->has($this->area) : false; } - private function matchDefaultRoute(Route $route): bool + private function defaultRouteDisabled(Route $route): bool { if (false === $this->options['disable_default_routes']) { return true; From 60dac6f7daad204a472f6959d5bd0987799b05c1 Mon Sep 17 00:00:00 2001 From: Frederik Holz Date: Mon, 6 Sep 2021 13:33:25 +0200 Subject: [PATCH 5/7] Fix codestyle issue --- Tests/Routing/FilteredRouteCollectionBuilderTest.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Tests/Routing/FilteredRouteCollectionBuilderTest.php b/Tests/Routing/FilteredRouteCollectionBuilderTest.php index 0545d535f..2c97fcbdc 100644 --- a/Tests/Routing/FilteredRouteCollectionBuilderTest.php +++ b/Tests/Routing/FilteredRouteCollectionBuilderTest.php @@ -250,7 +250,7 @@ public function getNonMatchingRoutes(): array /** * @dataProvider getMatchingRoutesWithDisabledDefaultRoutes * @param array $annotations - * @param array $options + * @param array $options */ public function testRoutesWithDisabledDefaultRoutes( string $name, From e3df317ee92007749b6e537258796a2fcb7d545a Mon Sep 17 00:00:00 2001 From: Frederik Holz Date: Mon, 6 Sep 2021 13:33:51 +0200 Subject: [PATCH 6/7] Fix codestyle issue --- Tests/Routing/FilteredRouteCollectionBuilderTest.php | 1 + 1 file changed, 1 insertion(+) diff --git a/Tests/Routing/FilteredRouteCollectionBuilderTest.php b/Tests/Routing/FilteredRouteCollectionBuilderTest.php index 2c97fcbdc..7cf5885d8 100644 --- a/Tests/Routing/FilteredRouteCollectionBuilderTest.php +++ b/Tests/Routing/FilteredRouteCollectionBuilderTest.php @@ -249,6 +249,7 @@ public function getNonMatchingRoutes(): array /** * @dataProvider getMatchingRoutesWithDisabledDefaultRoutes + * * @param array $annotations * @param array $options */ From 5647a95378dd268b577336d794183fba79379493 Mon Sep 17 00:00:00 2001 From: Frederik Holz Date: Mon, 6 Sep 2021 13:36:23 +0200 Subject: [PATCH 7/7] Change naming of dataProvider to match testing name --- Tests/Routing/FilteredRouteCollectionBuilderTest.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Tests/Routing/FilteredRouteCollectionBuilderTest.php b/Tests/Routing/FilteredRouteCollectionBuilderTest.php index 7cf5885d8..6148c1c05 100644 --- a/Tests/Routing/FilteredRouteCollectionBuilderTest.php +++ b/Tests/Routing/FilteredRouteCollectionBuilderTest.php @@ -248,7 +248,7 @@ public function getNonMatchingRoutes(): array } /** - * @dataProvider getMatchingRoutesWithDisabledDefaultRoutes + * @dataProvider getRoutesWithDisabledDefaultRoutes * * @param array $annotations * @param array $options @@ -288,7 +288,7 @@ public function testRoutesWithDisabledDefaultRoutes( /** * @return array */ - public function getMatchingRoutesWithDisabledDefaultRoutes(): array + public function getRoutesWithDisabledDefaultRoutes(): array { return [ 'non matching route without Annotation' => [