diff --git a/Command/DumpCommand.php b/Command/DumpCommand.php index 03bf3d9..f3ef969 100644 --- a/Command/DumpCommand.php +++ b/Command/DumpCommand.php @@ -34,6 +34,7 @@ class DumpCommand extends ContainerAwareCommand $this->availableFormats[0] ) ->addOption('no-sandbox', '', InputOption::VALUE_NONE) + ->addOption('api-version', null, InputOption::VALUE_REQUIRED, 'The API version') ->setName('api:doc:dump') ; } @@ -62,7 +63,11 @@ class DumpCommand extends ContainerAwareCommand $this->getContainer()->set('request', new Request(), 'request'); } - $extractedDoc = $this->getContainer()->get('nelmio_api_doc.extractor.api_doc_extractor')->all(); + $extractor = $this->getContainer()->get('nelmio_api_doc.extractor.api_doc_extractor') + $extractedDoc = $input->hasOption('api-version') ? + $extractor->allForVersion($input->getOption('api-version')) : + $extractor->all(); + $formattedDoc = $formatter->format($extractedDoc); if ('json' === $format) { diff --git a/Controller/ApiDocController.php b/Controller/ApiDocController.php index deebcf2..101902a 100644 --- a/Controller/ApiDocController.php +++ b/Controller/ApiDocController.php @@ -19,9 +19,12 @@ use Symfony\Component\HttpFoundation\Response; class ApiDocController extends Controller { - public function indexAction() + public function indexAction(Request $request) { - $extractedDoc = $this->get('nelmio_api_doc.extractor.api_doc_extractor')->all(); + $extractor = $this->get('nelmio_api_doc.extractor.api_doc_extractor'); + $apiVersion = $request->query->get('_version', null); + $extractedDoc = $apiVersion ? $extractor->allForVersion($apiVersion) : $extractor->all(); + $htmlContent = $this->get('nelmio_api_doc.formatter.html_formatter')->format($extractedDoc); return new Response($htmlContent, 200, array('Content-Type' => 'text/html')); diff --git a/Extractor/ApiDocExtractor.php b/Extractor/ApiDocExtractor.php index 56d6589..8c25202 100644 --- a/Extractor/ApiDocExtractor.php +++ b/Extractor/ApiDocExtractor.php @@ -88,6 +88,30 @@ class ApiDocExtractor return $this->extractAnnotations($this->getRoutes()); } + /** + * Extracts annotations from routes for specific version + * + * @param string $apiVersion API version + * + * @return array + */ + public function allForVersion($apiVersion) + { + $data = $this->all(); + + foreach ($data as $k => $a) { + // ignore other api version's routes + if ( + $a['annotation']->getRoute()->getDefault('_version') && + !version_compare($apiVersion, $a['annotation']->getRoute()->getDefault('_version'), '=') + ) { + unset($data[$k]); + } + } + + return $data; + } + /** * Returns an array of data where each data is an array with the following keys: * - annotation diff --git a/Extractor/CachingApiDocExtractor.php b/Extractor/CachingApiDocExtractor.php index a9c22d6..8304123 100644 --- a/Extractor/CachingApiDocExtractor.php +++ b/Extractor/CachingApiDocExtractor.php @@ -65,11 +65,11 @@ class CachingApiDocExtractor extends ApiDocExtractor $data = parent::all(); $this->cache->write(serialize($data), $resources); - + return $data; } return unserialize(file_get_contents($this->cacheFile)); } -} +} diff --git a/Resources/doc/index.md b/Resources/doc/index.md index 1f5ca00..a417cd5 100644 --- a/Resources/doc/index.md +++ b/Resources/doc/index.md @@ -384,6 +384,28 @@ It is a good idea to enable the internal caching mechanism on production: cache: enabled: true +### Route versions + +You can define version for the API routes: +```yml +api_v3_products_list: + pattern: /api/v3/products.{_format} + defaults: { _controller: NelmioApiDocTestBundle:Test:routeVersion, _format: json, _version: "3.0" } + requirements: + _method: GET + +api_v1_orders: + resource: "@AcmeOrderBundle/Resources/config/routing/orders_v1.yml" + defaults: { _version: "1.0" } + prefix: /api/v1/orders +``` + +And generate documentation for specific version by the command: + + php app/console api:doc:dump --format=html --api-version=3.0 > api.html + +Or by adding `?_version={version}` to API documentation page URL. + Configuration In-Depth ---------------------- diff --git a/Tests/Controller/ApiDocControllerTest.php b/Tests/Controller/ApiDocControllerTest.php index e3c955f..3b53b11 100644 --- a/Tests/Controller/ApiDocControllerTest.php +++ b/Tests/Controller/ApiDocControllerTest.php @@ -27,7 +27,6 @@ class ApiDocControllerTest extends WebTestCase $this->assertEquals(200, $response->getStatusCode()); $this->assertEquals('application/json', $response->headers->get('Content-type')); - } public function dataTestApiDeclarations() diff --git a/Tests/Extractor/ApiDocExtractorTest.php b/Tests/Extractor/ApiDocExtractorTest.php index b09d8ad..91732c3 100644 --- a/Tests/Extractor/ApiDocExtractorTest.php +++ b/Tests/Extractor/ApiDocExtractorTest.php @@ -17,7 +17,7 @@ use Nelmio\ApiDocBundle\Tests\WebTestCase; class ApiDocExtractorTest extends WebTestCase { - const ROUTES_QUANTITY = 33; + const ROUTES_QUANTITY = 34; public function testAll() { @@ -78,7 +78,21 @@ class ApiDocExtractorTest extends WebTestCase $a3 = $data[20]['annotation']; $this->assertTrue($a3->getHttps()); + } + public function testRouteVersionChecking() + { + $container = $this->getContainer(); + $extractor = $container->get('nelmio_api_doc.extractor.api_doc_extractor'); + $data = $extractor->allForVersion('1.5'); + + $this->assertTrue(is_array($data)); + $this->assertCount(self::ROUTES_QUANTITY, $data); + + $data = $extractor->allForVersion('1.4'); + + $this->assertTrue(is_array($data)); + $this->assertCount(self::ROUTES_QUANTITY - 1, $data); } public function testGet() diff --git a/Tests/Fixtures/Controller/TestController.php b/Tests/Fixtures/Controller/TestController.php index b71cadd..1b63a98 100644 --- a/Tests/Fixtures/Controller/TestController.php +++ b/Tests/Fixtures/Controller/TestController.php @@ -78,6 +78,13 @@ class TestController { } + /** + * @ApiDoc() + */ + public function routeVersionAction() + { + } + /** * @ApiDoc(description="Action without HTTP verb") */ diff --git a/Tests/Fixtures/app/config/routing.yml b/Tests/Fixtures/app/config/routing.yml index 9f24fc0..5ad147d 100644 --- a/Tests/Fixtures/app/config/routing.yml +++ b/Tests/Fixtures/app/config/routing.yml @@ -226,11 +226,16 @@ test_required_parameters: requirements: _method: POST _format: json|xml|html - + test_put_disables_required_parameters: pattern: /api/other-resources/{id}.{_format} defaults: { _controller: NelmioApiDocTestBundle:Resource:requiredParametersAction, _format: json } requirements: _method: PUT - _format: json|xml|html + _format: json|xml|html +test_route_version_checking: + pattern: /zz-tests-route-version.{_format} + defaults: { _controller: NelmioApiDocTestBundle:Test:routeVersion, _format: json, _version: "1.5" } + requirements: + _method: GET diff --git a/Tests/Formatter/MarkdownFormatterTest.php b/Tests/Formatter/MarkdownFormatterTest.php index 7b5caa1..b10853b 100644 --- a/Tests/Formatter/MarkdownFormatterTest.php +++ b/Tests/Formatter/MarkdownFormatterTest.php @@ -948,6 +948,15 @@ related[b]: ### `POST` /zsecured ### + + + +### `GET` /zz-tests-route-version.{_format} ### + + +#### Requirements #### + +**_format** MARKDOWN; $this->assertEquals($expected, $result); diff --git a/Tests/Formatter/SimpleFormatterTest.php b/Tests/Formatter/SimpleFormatterTest.php index 380b813..3ac7cff 100644 --- a/Tests/Formatter/SimpleFormatterTest.php +++ b/Tests/Formatter/SimpleFormatterTest.php @@ -1256,6 +1256,24 @@ With multiple lines.', 'https' => false, 'authenticationRoles' => array(), 'deprecated' => false + ), + 21 => + array( + 'authentication' => false, + 'method' => 'GET', + 'uri' => '/zz-tests-route-version.{_format}', + 'https' => false, + 'authenticationRoles' => array(), + 'deprecated' => false, + 'requirements' => + array( + '_format' => + array( + 'requirement' => '', + 'dataType' => '', + 'description' => '', + ), + ), ) ), '/tests2' =>