diff --git a/Extractor/ApiDocExtractor.php b/Extractor/ApiDocExtractor.php index 630ad4c..9e058c0 100644 --- a/Extractor/ApiDocExtractor.php +++ b/Extractor/ApiDocExtractor.php @@ -269,23 +269,23 @@ class ApiDocExtractor // input (populates 'parameters' for the formatters) if (null !== $input = $annotation->getInput()) { - $parameters = array(); - + $parameters = array(); $normalizedInput = $this->normalizeClassParameter($input); $supportedParsers = array(); - $parameters = array(); - foreach ($this->parsers as $parser) { + foreach ($this->getParsers($normalizedInput) as $parser) { if ($parser->supports($normalizedInput)) { $supportedParsers[] = $parser; - $parameters = $this->mergeParameters($parameters, $parser->parse($normalizedInput)); + $parameters = $this->mergeParameters($parameters, $parser->parse($normalizedInput)); } } foreach ($supportedParsers as $parser) { if ($parser instanceof PostParserInterface) { - $mp = $parser->postParse($normalizedInput, $parameters); - $parameters = $this->mergeParameters($parameters, $mp); + $parameters = $this->mergeParameters( + $parameters, + $parser->postParse($normalizedInput, $parameters) + ); } } @@ -303,15 +303,15 @@ class ApiDocExtractor // output (populates 'response' for the formatters) if (null !== $output = $annotation->getOutput()) { - $response = array(); - + $response = array(); $normalizedOutput = $this->normalizeClassParameter($output); - foreach ($this->parsers as $parser) { + foreach ($this->getParsers($normalizedOutput) as $parser) { if ($parser->supports($normalizedOutput)) { $response = $this->mergeParameters($response, $parser->parse($normalizedOutput)); } } + $response = $this->clearClasses($response); $annotation->setResponse($response); @@ -477,4 +477,20 @@ class ApiDocExtractor return $array; } + + private function getParsers(array $parameters) + { + if (isset($parameters['parsers'])) { + $parsers = array(); + foreach ($this->parsers as $parser) { + if (in_array(get_class($parser), $parameters['parsers'])) { + $parsers[] = $parser; + } + } + } else { + $parsers = $this->parsers; + } + + return $parsers; + } } diff --git a/Extractor/Handler/JmsSecurityExtraHandler.php b/Extractor/Handler/JmsSecurityExtraHandler.php index b1eeada..8aae3d1 100644 --- a/Extractor/Handler/JmsSecurityExtraHandler.php +++ b/Extractor/Handler/JmsSecurityExtraHandler.php @@ -24,7 +24,7 @@ class JmsSecurityExtraHandler implements HandlerInterface foreach ($annotations as $annot) { if ($annot instanceof PreAuthorize) { $annotation->setAuthentication(true); - } else if ($annot instanceof Secure) { + } elseif ($annot instanceof Secure) { $annotation->setAuthentication(true); $annotation->setAuthenticationRoles(is_array($annot->roles) ? $annot->roles : explode(',', $annot->roles)); } diff --git a/Parser/ValidationParser.php b/Parser/ValidationParser.php index 646b4f5..a7281e2 100644 --- a/Parser/ValidationParser.php +++ b/Parser/ValidationParser.php @@ -52,6 +52,7 @@ class ValidationParser implements ParserInterface, PostParserInterface public function parse(array $input) { $className = $input['class']; + return $this->doParse($className, array()); } @@ -62,7 +63,7 @@ class ValidationParser implements ParserInterface, PostParserInterface * @param array $visited * @return array */ - protected function doParse ($className, array $visited) + protected function doParse($className, array $visited) { $params = array(); $classdata = $this->factory->getMetadataFor($className); diff --git a/Resources/doc/index.md b/Resources/doc/index.md index b72f902..9e24950 100644 --- a/Resources/doc/index.md +++ b/Resources/doc/index.md @@ -229,7 +229,7 @@ you can specify which groups to use when generating the documentation by using t If your `output` classes use [versioning capabilities of JMS Serializer](http://jmsyst.com/libs/serializer/master/cookbook/exclusion_strategies#versioning-objects), the versioning information will be automatically used when generating the documentation. -#### Form Types features #### +#### Form Types Features #### If you use `FormFactoryInterface::createdNamed('', 'your_form_type'`, then by default the documentation will use the form type name as the prefix (`your_form_type[param]` ... instead of just `param`). @@ -243,6 +243,28 @@ input = { } ``` +#### Used Parsers #### + +By default, all registered parsers are used, but sometimes you may want to +define which parsers you want to use. The `parsers` attribute is used to +configure a list of parsers that will be used: + +``` +output={ + "class" = "Acme\Bundle\Entity\User", + "parsers" = { + "Nelmio\ApiDocBundle\Parser\JmsMetadataParser", + "Nelmio\ApiDocBundle\Parser\ValidationParser" + } +} +``` + +In this case the parsers `JmsMetadataParser` and `ValidationParser` are used to +generate returned data. + +This feature also works for both the `input` and `output` properties. + + ### Documentation on-the-fly ### By calling an URL with the parameter `?_doc=1`, you will get the corresponding documentation if available. diff --git a/Tests/Extractor/ApiDocExtractorTest.php b/Tests/Extractor/ApiDocExtractorTest.php index 7ecc676..e05a251 100644 --- a/Tests/Extractor/ApiDocExtractorTest.php +++ b/Tests/Extractor/ApiDocExtractorTest.php @@ -15,7 +15,7 @@ use Nelmio\ApiDocBundle\Tests\WebTestCase; class ApiDocExtractorTest extends WebTestCase { - const ROUTES_QUANTITY = 22; + const ROUTES_QUANTITY = 24; public function testAll() { @@ -215,4 +215,40 @@ class ApiDocExtractorTest extends WebTestCase $annotation->getDeprecated() ); } + + public function testOutputWithSelectedParsers() + { + $container = $this->getContainer(); + $extractor = $container->get('nelmio_api_doc.extractor.api_doc_extractor'); + $annotation = $extractor->get('Nelmio\ApiDocBundle\Tests\Fixtures\Controller\TestController::zReturnSelectedParsersOutputAction', 'test_route_19'); + + $this->assertNotNull($annotation); + $output = $annotation->getOutput(); + $parsers = $output['parsers']; + $this->assertEquals( + "Nelmio\\ApiDocBundle\\Parser\\JmsMetadataParser", + $parsers[0] + ); + $this->assertEquals( + "Nelmio\\ApiDocBundle\\Parser\\ValidationParser", + $parsers[1] + ); + $this->assertCount(2, $parsers); + } + + public function testInputWithSelectedParsers() + { + $container = $this->getContainer(); + $extractor = $container->get('nelmio_api_doc.extractor.api_doc_extractor'); + $annotation = $extractor->get('Nelmio\ApiDocBundle\Tests\Fixtures\Controller\TestController::zReturnSelectedParsersInputAction', 'test_route_20'); + + $this->assertNotNull($annotation); + $input = $annotation->getInput(); + $parsers = $input['parsers']; + $this->assertEquals( + "Nelmio\\ApiDocBundle\\Parser\\FormTypeParser", + $parsers[0] + ); + $this->assertCount(1, $parsers); + } } diff --git a/Tests/Fixtures/Controller/TestController.php b/Tests/Fixtures/Controller/TestController.php index eb80a96..2742ef4 100644 --- a/Tests/Fixtures/Controller/TestController.php +++ b/Tests/Fixtures/Controller/TestController.php @@ -223,4 +223,33 @@ class TestController public function cgetAction($id) { } + + /** + * @ApiDoc( + * input={ + * "class"="Nelmio\ApiDocBundle\Tests\Fixtures\Form\TestType", + * "parsers"={ + * "Nelmio\ApiDocBundle\Parser\FormTypeParser", + * } + * } + * ) + */ + public function zReturnSelectedParsersInputAction() + { + } + + /** + * @ApiDoc( + * output={ + * "class"="Nelmio\ApiDocBundle\Tests\Fixtures\Model\MultipleTest", + * "parsers"={ + * "Nelmio\ApiDocBundle\Parser\JmsMetadataParser", + * "Nelmio\ApiDocBundle\Parser\ValidationParser" + * } + * } + * ) + */ + public function zReturnSelectedParsersOutputAction() + { + } } diff --git a/Tests/Fixtures/app/config/routing.yml b/Tests/Fixtures/app/config/routing.yml index 0b3ba08..38b259e 100644 --- a/Tests/Fixtures/app/config/routing.yml +++ b/Tests/Fixtures/app/config/routing.yml @@ -129,3 +129,11 @@ test_route_18: test_route_named_resource: pattern: /named-resource defaults: { _controller: NelmioApiDocTestBundle:Test:namedResource } + +test_route_19: + pattern: /z-return-selected-parsers-output + defaults: { _controller: NelmioApiDocTestBundle:Test:zReturnSelectedParsersOutput } + +test_route_20: + pattern: /z-return-selected-parsers-input + defaults: { _controller: NelmioApiDocTestBundle:Test:zReturnSelectedParsersInput } diff --git a/Tests/Formatter/MarkdownFormatterTest.php b/Tests/Formatter/MarkdownFormatterTest.php index af6b3f6..31a894a 100644 --- a/Tests/Formatter/MarkdownFormatterTest.php +++ b/Tests/Formatter/MarkdownFormatterTest.php @@ -473,6 +473,54 @@ param1: ### `ANY` /z-return-jms-and-validator-output ### +#### Response #### + +bar: + + * type: DateTime + +objects[]: + + * type: array of objects (Test) + +objects[][a]: + + * type: string + +objects[][b]: + + * type: DateTime + +number: + + * type: DateTime + + +### `ANY` /z-return-selected-parsers-input ### + + +#### Parameters #### + +a: + + * type: string + * required: true + * description: A nice description + +b: + + * type: string + * required: false + +c: + + * type: boolean + * required: true + + +### `ANY` /z-return-selected-parsers-output ### + + #### Response #### bar: diff --git a/Tests/Formatter/SimpleFormatterTest.php b/Tests/Formatter/SimpleFormatterTest.php index 0ef745c..d696b21 100644 --- a/Tests/Formatter/SimpleFormatterTest.php +++ b/Tests/Formatter/SimpleFormatterTest.php @@ -861,6 +861,78 @@ With multiple lines.', ) ), 'authenticationRoles' => array(), + ), + 16 => + array( + 'method' => "ANY", + 'uri' => "/z-return-selected-parsers-input", + 'https' => false, + 'authentication' => false, + 'deprecated' => false, + 'authenticationRoles' => array(), + 'parameters' => + array( + 'a' => array( + 'dataType' => 'string', + 'required' => true, + 'description' => 'A nice description', + 'readonly' => false, + ), + 'b' => array( + 'dataType' => 'string', + 'required' => false, + 'description' => '', + 'readonly' => false, + ), + 'c' => array( + 'dataType' => 'boolean', + 'required' => true, + 'description' => '', + 'readonly' => false, + ), + ) + ), + 17 => + array( + 'method' => "ANY", + 'uri' => "/z-return-selected-parsers-output", + 'https' => false, + 'authentication' => false, + 'deprecated' => false, + 'response' => array ( + 'bar' => array( + 'dataType' => 'DateTime', + 'required' => null, + 'readonly' => null + ), + 'number' => array( + 'dataType' => 'DateTime', + 'required' => false, + 'description' => '', + 'readonly' => false, + 'sinceVersion' => null, + 'untilVersion' => null + ), + 'objects' => array( + 'dataType' => 'array of objects (Test)', + 'readonly' => null, + 'required' => null, + 'children' => array( + 'a' => array( + 'dataType' => 'string', + 'format' => '{length: min: foo}, {not blank}', + 'required' => true, + 'readonly' => null + ), + 'b' => array( + 'dataType' => 'DateTime', + 'required' => null, + 'readonly' => null + ) + ) + ) + ), + 'authenticationRoles' => array(), ) ), '/tests2' =>