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..a7cb54530 100644 --- a/DependencyInjection/NelmioApiDocExtension.php +++ b/DependencyInjection/NelmioApiDocExtension.php @@ -106,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/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 diff --git a/Routing/FilteredRouteCollectionBuilder.php b/Routing/FilteredRouteCollectionBuilder.php index 7e989eeda..97294944b 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)) { @@ -73,6 +75,7 @@ public function filter(RouteCollection $routes): RouteCollection && $this->matchHost($route) && $this->matchAnnotation($route) && $this->matchName($name) + && $this->defaultRouteDisabled($route) ) { $filteredRoutes->add($name, $route); } @@ -135,4 +138,31 @@ private function matchAnnotation(Route $route): bool return (null !== $areas) ? $areas->has($this->area) : false; } + + private function defaultRouteDisabled(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/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' => [], ], ], diff --git a/Tests/Routing/FilteredRouteCollectionBuilderTest.php b/Tests/Routing/FilteredRouteCollectionBuilderTest.php index 744e5234e..6148c1c05 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,74 @@ public function getNonMatchingRoutes(): array ]; } + /** + * @dataProvider getRoutesWithDisabledDefaultRoutes + * + * @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 getRoutesWithDisabledDefaultRoutes(): 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)) {