From b71bc8bf3f94007559d4184357e4d4967ddca13d Mon Sep 17 00:00:00 2001 From: Evan Villemez Date: Tue, 7 Aug 2012 17:50:58 -0400 Subject: [PATCH 1/5] started on implementing JmsMetadataParser and tests --- DependencyInjection/NelmioApiDocExtension.php | 10 ++ Extractor/ApiDocExtractor.php | 1 + Parser/JmsMetadataParser.php | 93 +++++++++++++++++++ README.md | 4 +- Resources/config/services.jms.xml | 16 ++++ TODO.md | 5 + Tests/Extractor/ApiDocExtratorTest.php | 2 +- Tests/Fixtures/Controller/TestController.php | 10 ++ Tests/Fixtures/Model/JmsTest.php | 33 +++++++ Tests/Fixtures/app/AppKernel.php | 1 + Tests/Fixtures/app/config/default.yml | 30 ++++++ Tests/Fixtures/app/config/routing.yml | 7 ++ Tests/Formatter/MarkdownFormatterTest.php | 5 + Tests/Formatter/SimpleFormatterTest.php | 41 +++++++- composer.json | 2 +- 15 files changed, 255 insertions(+), 5 deletions(-) create mode 100644 Parser/JmsMetadataParser.php create mode 100644 Resources/config/services.jms.xml create mode 100644 TODO.md create mode 100644 Tests/Fixtures/Model/JmsTest.php diff --git a/DependencyInjection/NelmioApiDocExtension.php b/DependencyInjection/NelmioApiDocExtension.php index 1ecae53..5aedd86 100644 --- a/DependencyInjection/NelmioApiDocExtension.php +++ b/DependencyInjection/NelmioApiDocExtension.php @@ -36,5 +36,15 @@ class NelmioApiDocExtension extends Extension $loader->load('formatters.xml'); $loader->load('request_listener.xml'); $loader->load('services.xml'); + + //JMS may or may not be installed, if it is, load that config as well + try { + if ($serializer = $container->findDefinition('serializer')) { + die(__METHOD__); + $loader->load('services.jms.xml'); + } + } catch (\Exception $e) { + + } } } diff --git a/Extractor/ApiDocExtractor.php b/Extractor/ApiDocExtractor.php index dcc8af1..2e0dae6 100644 --- a/Extractor/ApiDocExtractor.php +++ b/Extractor/ApiDocExtractor.php @@ -229,6 +229,7 @@ class ApiDocExtractor foreach ($this->parsers as $parser) { if ($parser->supports($input)) { $parameters = $parser->parse($input); + break; } } diff --git a/Parser/JmsMetadataParser.php b/Parser/JmsMetadataParser.php new file mode 100644 index 0000000..8978de2 --- /dev/null +++ b/Parser/JmsMetadataParser.php @@ -0,0 +1,93 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Nelmio\ApiDocBundle\Parser; + +use Metadata\MetadataFactoryInterface; + +/** + * Uses the JMS metadata factory to extract input/output model information + */ +class JmsMetadataParser implements ParserInterface +{ + + /** + * Constructor, requires JMS Metadata factory + */ + public function __construct(MetadataFactoryInterface $factory) + { + $this->factory = $factory; + } + + /** + * {@inheritdoc} + */ + public function supports($input) + { + if($meta = $this->factory->getMetadataForClass($input)) { + return true; + } + + return false; + } + + /** + * {@inheritdoc} + */ + public function parse($input) + { + die(__METHOD__); + + $meta = $this->factory->getMetadataForClass($input); + + if(is_null($meta)) { + throw new \InvalidArgumentException(sprintf("No metadata found for class %s", $input)); + } + + $params = array(); + $refClass = new \ReflectionClass($input); + + //iterate over property metadata + foreach ($meta->propertyMetadata as $item) { + $name = isset($item->serializedName) ? $item->serializedName : $item->name; + + $type = $this->getType($item->type); + + if (true) { + //TODO: check for nested type + } + + $params[$name] = array( + 'dataType' => $item->type, + 'required' => false, //can't think of a good way to specify this one, JMS doesn't have a setting for this + 'description' => $this->getDescription($refClass, $item->name), + 'readonly' => $item->readonly + ); + } + + return $params; + } + + protected function getDescription($ref, $nativePropertyName) + { + $description = "No description."; + + if (!$doc = $ref->getProperty($nativePropertyName)->getDocComment()) { + return $description; + } + + //TODO: regex comment to get description - or move doc comment parsing functionality from `ApiDocExtractor` to a new location + //in order to reuse it here + + return $description; + } + +} diff --git a/README.md b/README.md index ae19234..d011f4b 100644 --- a/README.md +++ b/README.md @@ -99,7 +99,7 @@ The following properties are available: * `filters`: an array of filters; -* `input`: the input type associated to the method, currently this only supports Form Types, useful for POST|PUT methods, either as FQCN or +* `input`: the input type associated to the method, currently this Form Types, and classes with JMS Serializer metadata, useful for POST|PUT methods, either as FQCN or as form type (if it is registered in the form factory in the container) Each _filter_ has to define a `name` parameter, but other parameters are free. Filters are often optional @@ -110,6 +110,8 @@ and determines for each parameter its data type, and if it's required or not. For Form Types, you can add an extra option named `description` on each field: +For classes parsed with JMS metadata, description will be taken from the properties doc comment, if available. + ``` php + + + + Nelmio\ApiDocBundle\Parser\JmsMetadataParser + + + + + + diff --git a/TODO.md b/TODO.md new file mode 100644 index 0000000..f4d3408 --- /dev/null +++ b/TODO.md @@ -0,0 +1,5 @@ +# TODO # + +* Fix JMS registration by moving logic into a CompilerPass +* Finish `JmsMetadataParser` +* Implement new tests \ No newline at end of file diff --git a/Tests/Extractor/ApiDocExtratorTest.php b/Tests/Extractor/ApiDocExtratorTest.php index b092643..a15de3a 100644 --- a/Tests/Extractor/ApiDocExtratorTest.php +++ b/Tests/Extractor/ApiDocExtratorTest.php @@ -22,7 +22,7 @@ class ApiDocExtractorTest extends WebTestCase $data = $extractor->all(); $this->assertTrue(is_array($data)); - $this->assertCount(10, $data); + $this->assertCount(11, $data); foreach ($data as $d) { $this->assertTrue(is_array($d)); diff --git a/Tests/Fixtures/Controller/TestController.php b/Tests/Fixtures/Controller/TestController.php index d39a02a..e01189d 100644 --- a/Tests/Fixtures/Controller/TestController.php +++ b/Tests/Fixtures/Controller/TestController.php @@ -90,4 +90,14 @@ class TestController public function zActionWithQueryParamAction() { } + + /** + * @ApiDoc( + * description="Testing JMS", + * input="Nelmio\ApiDocBundle\Tests\Fixtures\Model\JmsTest" + * ) + */ + public function jmsInputTestAction() + { + } } diff --git a/Tests/Fixtures/Model/JmsTest.php b/Tests/Fixtures/Model/JmsTest.php new file mode 100644 index 0000000..343d936 --- /dev/null +++ b/Tests/Fixtures/Model/JmsTest.php @@ -0,0 +1,33 @@ + 'Action without HTTP verb', ), 3 => + array( + 'method' => 'POST', + 'uri' => '/jms-input-test', + 'parameters' => + array( + 'foo' => + array( + 'dataType' => 'string', + 'required' => false, + 'description' => 'No description.', + 'readonly' => false + ), + 'bar' => + array( + 'dataType' => 'DateTime', + 'required' => false, + 'description' => 'No description.', + 'readonly' => true + ), + 'number' => + array( + 'dataType' => 'double', + 'required' => false, + 'description' => 'No description.', + 'readonly' => false + ), + 'arr' => + array( + 'dataType' => 'array', + 'required' => false, + 'description' => 'No description.', + 'readonly' => false + ) + ), + 'description' => 'Testing JMS' + ), + 4 => array( 'method' => 'ANY', 'uri' => '/my-commented/{id}/{page}', @@ -177,7 +214,7 @@ class SimpleFormatterTest extends WebTestCase 'description' => 'This method is useful to test if the getDocComment works.', 'documentation' => "This method is useful to test if the getDocComment works.\nAnd, it supports multilines until the first '@' char." ), - 4 => + 5 => array( 'method' => 'ANY', 'uri' => '/yet-another/{id}', @@ -186,7 +223,7 @@ class SimpleFormatterTest extends WebTestCase 'id' => array('type' => '', 'description' => '', 'requirement' => '\d+') ), ), - 5 => + 6 => array( 'method' => 'GET', 'uri' => '/z-action-with-query-param', diff --git a/composer.json b/composer.json index 2f8d743..80ed4d6 100644 --- a/composer.json +++ b/composer.json @@ -25,7 +25,7 @@ "symfony/browser-kit": "2.1.*", "symfony/validator": "2.1.*", "symfony/yaml": "2.1.*", - "friendsofsymfony/rest-bundle": "dev-master" + "jms/serializer-bundle": "0.9.*" }, "minimum-stability": "dev", "autoload": { From 149f282481997204e8ae760d656d49ee568792f2 Mon Sep 17 00:00:00 2001 From: Evan Villemez Date: Tue, 7 Aug 2012 21:47:33 -0400 Subject: [PATCH 2/5] tests passing with JmsMetadataParser, but work still to do on required and description properties --- DependencyInjection/NelmioApiDocExtension.php | 12 +------ DependencyInjection/RegisterJmsParserPass.php | 27 ++++++++++++++++ NelmioApiDocBundle.php | 2 ++ Parser/JmsMetadataParser.php | 32 +++++++------------ TODO.md | 5 --- Tests/Formatter/MarkdownFormatterTest.php | 26 +++++++++++++++ 6 files changed, 68 insertions(+), 36 deletions(-) create mode 100644 DependencyInjection/RegisterJmsParserPass.php delete mode 100644 TODO.md diff --git a/DependencyInjection/NelmioApiDocExtension.php b/DependencyInjection/NelmioApiDocExtension.php index 5aedd86..abf129a 100644 --- a/DependencyInjection/NelmioApiDocExtension.php +++ b/DependencyInjection/NelmioApiDocExtension.php @@ -35,16 +35,6 @@ class NelmioApiDocExtension extends Extension $loader = new XmlFileLoader($container, new FileLocator(__DIR__.'/../Resources/config')); $loader->load('formatters.xml'); $loader->load('request_listener.xml'); - $loader->load('services.xml'); - - //JMS may or may not be installed, if it is, load that config as well - try { - if ($serializer = $container->findDefinition('serializer')) { - die(__METHOD__); - $loader->load('services.jms.xml'); - } - } catch (\Exception $e) { - - } + $loader->load('services.xml'); } } diff --git a/DependencyInjection/RegisterJmsParserPass.php b/DependencyInjection/RegisterJmsParserPass.php new file mode 100644 index 0000000..133610f --- /dev/null +++ b/DependencyInjection/RegisterJmsParserPass.php @@ -0,0 +1,27 @@ +findDefinition('serializer')) { + $loader->load('services.jms.xml'); + } + } catch (\Exception $e) { + } + + } +} diff --git a/NelmioApiDocBundle.php b/NelmioApiDocBundle.php index fbfe504..ad9a5ce 100644 --- a/NelmioApiDocBundle.php +++ b/NelmioApiDocBundle.php @@ -4,6 +4,7 @@ namespace Nelmio\ApiDocBundle; use Symfony\Component\HttpKernel\Bundle\Bundle; use Symfony\Component\DependencyInjection\ContainerBuilder; +use Nelmio\ApiDocBundle\DependencyInjection\RegisterJmsParserPass; use Nelmio\ApiDocBundle\DependencyInjection\RegisterExtractorParsersPass; class NelmioApiDocBundle extends Bundle @@ -12,6 +13,7 @@ class NelmioApiDocBundle extends Bundle { parent::build($container); + $container->addCompilerPass(new RegisterJmsParserPass()); $container->addCompilerPass(new RegisterExtractorParsersPass()); } } diff --git a/Parser/JmsMetadataParser.php b/Parser/JmsMetadataParser.php index 8978de2..d9cb507 100644 --- a/Parser/JmsMetadataParser.php +++ b/Parser/JmsMetadataParser.php @@ -44,8 +44,6 @@ class JmsMetadataParser implements ParserInterface */ public function parse($input) { - die(__METHOD__); - $meta = $this->factory->getMetadataForClass($input); if(is_null($meta)) { @@ -53,41 +51,35 @@ class JmsMetadataParser implements ParserInterface } $params = array(); - $refClass = new \ReflectionClass($input); //iterate over property metadata foreach ($meta->propertyMetadata as $item) { - $name = isset($item->serializedName) ? $item->serializedName : $item->name; - $type = $this->getType($item->type); - - if (true) { + if (!is_null($item->type)) { + $name = isset($item->serializedName) ? $item->serializedName : $item->name; + //TODO: check for nested type - } - $params[$name] = array( - 'dataType' => $item->type, - 'required' => false, //can't think of a good way to specify this one, JMS doesn't have a setting for this - 'description' => $this->getDescription($refClass, $item->name), - 'readonly' => $item->readonly - ); + $params[$name] = array( + 'dataType' => $item->type, + 'required' => false, //TODO: can't think of a good way to specify this one, JMS doesn't have a setting for this + 'description' => $this->getDescription($input, $item->name), + 'readonly' => $item->readOnly + ); + } } return $params; } - protected function getDescription($ref, $nativePropertyName) + protected function getDescription($className, $propertyName) { $description = "No description."; - if (!$doc = $ref->getProperty($nativePropertyName)->getDocComment()) { - return $description; - } - //TODO: regex comment to get description - or move doc comment parsing functionality from `ApiDocExtractor` to a new location //in order to reuse it here return $description; } - + } diff --git a/TODO.md b/TODO.md deleted file mode 100644 index f4d3408..0000000 --- a/TODO.md +++ /dev/null @@ -1,5 +0,0 @@ -# TODO # - -* Fix JMS registration by moving logic into a CompilerPass -* Finish `JmsMetadataParser` -* Implement new tests \ No newline at end of file diff --git a/Tests/Formatter/MarkdownFormatterTest.php b/Tests/Formatter/MarkdownFormatterTest.php index 3e5653b..be436b5 100644 --- a/Tests/Formatter/MarkdownFormatterTest.php +++ b/Tests/Formatter/MarkdownFormatterTest.php @@ -139,6 +139,32 @@ _Action without HTTP verb_ _Testing JMS_ +#### Parameters #### + +foo: + + * type: string + * required: false + * description: No description. + +bar: + + * type: DateTime + * required: false + * description: No description. + +number: + + * type: double + * required: false + * description: No description. + +arr: + + * type: array + * required: false + * description: No description. + ### `ANY` /my-commented/{id}/{page} ### From 68767f6c72ed8e9688b12fb2a92e43f5661c3b9d Mon Sep 17 00:00:00 2001 From: Evan Villemez Date: Tue, 7 Aug 2012 21:57:36 -0400 Subject: [PATCH 3/5] fixed cs --- DependencyInjection/NelmioApiDocExtension.php | 2 +- DependencyInjection/RegisterJmsParserPass.php | 4 +-- Parser/JmsMetadataParser.php | 27 +++++++++---------- Tests/Fixtures/Controller/TestController.php | 2 +- Tests/Fixtures/Model/JmsTest.php | 18 ++++++------- Tests/Fixtures/app/config/default.yml | 1 - Tests/Fixtures/app/config/routing.yml | 1 - 7 files changed, 25 insertions(+), 30 deletions(-) diff --git a/DependencyInjection/NelmioApiDocExtension.php b/DependencyInjection/NelmioApiDocExtension.php index abf129a..1ecae53 100644 --- a/DependencyInjection/NelmioApiDocExtension.php +++ b/DependencyInjection/NelmioApiDocExtension.php @@ -35,6 +35,6 @@ class NelmioApiDocExtension extends Extension $loader = new XmlFileLoader($container, new FileLocator(__DIR__.'/../Resources/config')); $loader->load('formatters.xml'); $loader->load('request_listener.xml'); - $loader->load('services.xml'); + $loader->load('services.xml'); } } diff --git a/DependencyInjection/RegisterJmsParserPass.php b/DependencyInjection/RegisterJmsParserPass.php index 133610f..81af794 100644 --- a/DependencyInjection/RegisterJmsParserPass.php +++ b/DependencyInjection/RegisterJmsParserPass.php @@ -4,9 +4,7 @@ namespace Nelmio\ApiDocBundle\DependencyInjection; use Symfony\Component\DependencyInjection\ContainerBuilder; use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface; -use Symfony\Component\DependencyInjection\Reference; use Symfony\Component\DependencyInjection\Loader\XmlFileLoader; -use Symfony\Component\Config\Definition\Processor; use Symfony\Component\Config\FileLocator; class RegisterJmsParserPass implements CompilerPassInterface @@ -14,7 +12,7 @@ class RegisterJmsParserPass implements CompilerPassInterface public function process(ContainerBuilder $container) { $loader = new XmlFileLoader($container, new FileLocator(__DIR__.'/../Resources/config')); - + //JMS may or may not be installed, if it is, load that config as well try { if ($serializer = $container->findDefinition('serializer')) { diff --git a/Parser/JmsMetadataParser.php b/Parser/JmsMetadataParser.php index d9cb507..996c8db 100644 --- a/Parser/JmsMetadataParser.php +++ b/Parser/JmsMetadataParser.php @@ -18,7 +18,7 @@ use Metadata\MetadataFactoryInterface; */ class JmsMetadataParser implements ParserInterface { - + /** * Constructor, requires JMS Metadata factory */ @@ -26,19 +26,19 @@ class JmsMetadataParser implements ParserInterface { $this->factory = $factory; } - + /** * {@inheritdoc} */ public function supports($input) { - if($meta = $this->factory->getMetadataForClass($input)) { + if ($meta = $this->factory->getMetadataForClass($input)) { return true; } - + return false; } - + /** * {@inheritdoc} */ @@ -46,20 +46,20 @@ class JmsMetadataParser implements ParserInterface { $meta = $this->factory->getMetadataForClass($input); - if(is_null($meta)) { + if (is_null($meta)) { throw new \InvalidArgumentException(sprintf("No metadata found for class %s", $input)); } - + $params = array(); - + //iterate over property metadata foreach ($meta->propertyMetadata as $item) { - + if (!is_null($item->type)) { $name = isset($item->serializedName) ? $item->serializedName : $item->name; - + //TODO: check for nested type - + $params[$name] = array( 'dataType' => $item->type, 'required' => false, //TODO: can't think of a good way to specify this one, JMS doesn't have a setting for this @@ -68,17 +68,16 @@ class JmsMetadataParser implements ParserInterface ); } } - + return $params; } - + protected function getDescription($className, $propertyName) { $description = "No description."; //TODO: regex comment to get description - or move doc comment parsing functionality from `ApiDocExtractor` to a new location //in order to reuse it here - return $description; } diff --git a/Tests/Fixtures/Controller/TestController.php b/Tests/Fixtures/Controller/TestController.php index e01189d..16c8e04 100644 --- a/Tests/Fixtures/Controller/TestController.php +++ b/Tests/Fixtures/Controller/TestController.php @@ -90,7 +90,7 @@ class TestController public function zActionWithQueryParamAction() { } - + /** * @ApiDoc( * description="Testing JMS", diff --git a/Tests/Fixtures/Model/JmsTest.php b/Tests/Fixtures/Model/JmsTest.php index 343d936..ff44443 100644 --- a/Tests/Fixtures/Model/JmsTest.php +++ b/Tests/Fixtures/Model/JmsTest.php @@ -7,27 +7,27 @@ use JMS\SerializerBundle\Annotation as JMS; class JmsTest { public $nothing; - - /** - * @JMS\Type("string"); - */ - public $foo; - + + /** + * @JMS\Type("string"); + */ + public $foo; + /** * @JMS\Type("DateTime"); * @JMS\ReadOnly */ public $bar; - + /** * @JMS\Type("double"); * @JMS\SerializedName("number"); */ public $baz; - + /** * @JMS\Type("array"); */ public $arr; - + } diff --git a/Tests/Fixtures/app/config/default.yml b/Tests/Fixtures/app/config/default.yml index a046f02..6c822c9 100644 --- a/Tests/Fixtures/app/config/default.yml +++ b/Tests/Fixtures/app/config/default.yml @@ -55,4 +55,3 @@ jms_serializer: # class: My\FooBundle\Entity\User # expected path: @MyFooBundle/Resources/config/serializer/Entity.User.(yml|xml|php) auto_detection: true - diff --git a/Tests/Fixtures/app/config/routing.yml b/Tests/Fixtures/app/config/routing.yml index f451e2b..cad9f93 100644 --- a/Tests/Fixtures/app/config/routing.yml +++ b/Tests/Fixtures/app/config/routing.yml @@ -69,4 +69,3 @@ test_service_route_4: NelmioApiDocBundle: resource: "@NelmioApiDocBundle/Resources/config/routing.yml" prefix: / - \ No newline at end of file From 034e13a20ef38ea13154cc47c0f2bdd9509aaded Mon Sep 17 00:00:00 2001 From: Evan Villemez Date: Tue, 7 Aug 2012 22:09:23 -0400 Subject: [PATCH 4/5] restored fosrestbundle to composer.json --- composer.json | 1 + 1 file changed, 1 insertion(+) diff --git a/composer.json b/composer.json index 80ed4d6..b198ee1 100644 --- a/composer.json +++ b/composer.json @@ -25,6 +25,7 @@ "symfony/browser-kit": "2.1.*", "symfony/validator": "2.1.*", "symfony/yaml": "2.1.*", + "friendsofsymfony/rest-bundle": "dev-master", "jms/serializer-bundle": "0.9.*" }, "minimum-stability": "dev", From 1cf7e18d614255d2de10601cc6dee1bf1311ead0 Mon Sep 17 00:00:00 2001 From: Evan Villemez Date: Wed, 8 Aug 2012 10:21:56 -0400 Subject: [PATCH 5/5] fixed README, other minor things --- DependencyInjection/RegisterJmsParserPass.php | 8 ++------ Parser/JmsMetadataParser.php | 3 ++- README.md | 4 ++-- 3 files changed, 6 insertions(+), 9 deletions(-) diff --git a/DependencyInjection/RegisterJmsParserPass.php b/DependencyInjection/RegisterJmsParserPass.php index 81af794..7c61460 100644 --- a/DependencyInjection/RegisterJmsParserPass.php +++ b/DependencyInjection/RegisterJmsParserPass.php @@ -14,12 +14,8 @@ class RegisterJmsParserPass implements CompilerPassInterface $loader = new XmlFileLoader($container, new FileLocator(__DIR__.'/../Resources/config')); //JMS may or may not be installed, if it is, load that config as well - try { - if ($serializer = $container->findDefinition('serializer')) { - $loader->load('services.jms.xml'); - } - } catch (\Exception $e) { + if ($container->hasDefinition('jms_serializer.serializer')) { + $loader->load('services.jms.xml'); } - } } diff --git a/Parser/JmsMetadataParser.php b/Parser/JmsMetadataParser.php index 996c8db..24594ec 100644 --- a/Parser/JmsMetadataParser.php +++ b/Parser/JmsMetadataParser.php @@ -46,7 +46,7 @@ class JmsMetadataParser implements ParserInterface { $meta = $this->factory->getMetadataForClass($input); - if (is_null($meta)) { + if (null === $meta) { throw new \InvalidArgumentException(sprintf("No metadata found for class %s", $input)); } @@ -78,6 +78,7 @@ class JmsMetadataParser implements ParserInterface //TODO: regex comment to get description - or move doc comment parsing functionality from `ApiDocExtractor` to a new location //in order to reuse it here + return $description; } diff --git a/README.md b/README.md index d011f4b..46f8dd4 100644 --- a/README.md +++ b/README.md @@ -99,8 +99,8 @@ The following properties are available: * `filters`: an array of filters; -* `input`: the input type associated to the method, currently this Form Types, and classes with JMS Serializer metadata, useful for POST|PUT methods, either as FQCN or - as form type (if it is registered in the form factory in the container) +* `input`: the input type associated to the method, currently this supports Form Types, and classes with JMS Serializer + metadata, useful for POST|PUT methods, either as FQCN or as form type (if it is registered in the form factory in the container) Each _filter_ has to define a `name` parameter, but other parameters are free. Filters are often optional parameters, and you can document them as you want, but keep in mind to be consistent for the whole documentation.