diff --git a/Extractor/ApiDocExtractor.php b/Extractor/ApiDocExtractor.php
index b9077e2..720a593 100644
--- a/Extractor/ApiDocExtractor.php
+++ b/Extractor/ApiDocExtractor.php
@@ -266,9 +266,11 @@ class ApiDocExtractor
if (null !== $input = $annotation->getInput()) {
$parameters = array();
+ $normalizedInput = $this->normalizeClassParameter($input);
+
foreach ($this->parsers as $parser) {
- if ($parser->supports($input)) {
- $parameters = $parser->parse($input);
+ if ($parser->supports($normalizedInput)) {
+ $parameters = $parser->parse($normalizedInput);
break;
}
}
@@ -287,9 +289,11 @@ class ApiDocExtractor
if (null !== $output = $annotation->getOutput()) {
$response = array();
+ $normalizedOutput = $this->normalizeClassParameter($output);
+
foreach ($this->parsers as $parser) {
- if ($parser->supports($output)) {
- $response = $parser->parse($output);
+ if ($parser->supports($normalizedOutput)) {
+ $response = $parser->parse($normalizedOutput);
break;
}
}
@@ -350,6 +354,26 @@ class ApiDocExtractor
return $annotation;
}
+ protected function normalizeClassParameter($input)
+ {
+ $defaults = array(
+ 'class' => '',
+ 'groups' => array(),
+ );
+
+ // normalize strings
+ if (is_string($input)) {
+ $input = array('class' => $input);
+ }
+
+ // normalize groups
+ if (isset($input['groups']) && is_string($input['groups'])) {
+ $input['groups'] = array_map('trim', explode(',', $input['groups']));
+ }
+
+ return array_merge($defaults, $input);
+ }
+
/**
* Parses annotations for a given method, and adds new information to the given ApiDoc
* annotation. Useful to extract information from the FOSRestBundle annotations.
diff --git a/Formatter/AbstractFormatter.php b/Formatter/AbstractFormatter.php
index 7935c0c..b5d2fc7 100644
--- a/Formatter/AbstractFormatter.php
+++ b/Formatter/AbstractFormatter.php
@@ -73,6 +73,8 @@ abstract class AbstractFormatter implements FormatterInterface
'dataType' => $info['dataType'],
'readonly' => $info['readonly'],
'required' => $info['required'],
+ 'sinceVersion' => array_key_exists('sinceVersion', $info) ? $info['sinceVersion'] : null,
+ 'untilVersion' => array_key_exists('untilVersion', $info) ? $info['untilVersion'] : null,
);
if (isset($info['children']) && (!$info['readonly'] || !$ignoreNestedReadOnly)) {
diff --git a/Formatter/MarkdownFormatter.php b/Formatter/MarkdownFormatter.php
index b4865ba..4bfe286 100644
--- a/Formatter/MarkdownFormatter.php
+++ b/Formatter/MarkdownFormatter.php
@@ -103,6 +103,20 @@ class MarkdownFormatter extends AbstractFormatter
$markdown .= sprintf(" * description: %s\n", $parameter['description']);
}
+ if (null !== $parameter['sinceVersion'] || null !== $parameter['untilVersion']) {
+ $markdown .= " * versions: ";
+ if ($parameter['sinceVersion']) {
+ $markdown .= '>='.$parameter['sinceVersion'];
+ }
+ if ($parameter['untilVersion']) {
+ if ($parameter['sinceVersion']) {
+ $markdown .= ',';
+ }
+ $markdown .= '<='.$parameter['untilVersion'];
+ }
+ $markdown .= "\n";
+ }
+
$markdown .= "\n";
}
}
diff --git a/Parser/FormTypeParser.php b/Parser/FormTypeParser.php
index e0f0a60..f04a2d9 100644
--- a/Parser/FormTypeParser.php
+++ b/Parser/FormTypeParser.php
@@ -47,10 +47,12 @@ class FormTypeParser implements ParserInterface
/**
* {@inheritdoc}
*/
- public function supports($item)
+ public function supports(array $item)
{
+ $className = $item['class'];
+
try {
- if ($this->createForm($item)) {
+ if ($this->createForm($className)) {
return true;
}
} catch (FormException $e) {
@@ -65,8 +67,10 @@ class FormTypeParser implements ParserInterface
/**
* {@inheritdoc}
*/
- public function parse($type)
+ public function parse(array $item)
{
+ $type = $item['class'];
+
if ($this->implementsType($type)) {
$type = $this->getTypeInstance($type);
}
diff --git a/Parser/JmsMetadataParser.php b/Parser/JmsMetadataParser.php
index 5ac634a..8286e20 100644
--- a/Parser/JmsMetadataParser.php
+++ b/Parser/JmsMetadataParser.php
@@ -11,6 +11,8 @@
namespace Nelmio\ApiDocBundle\Parser;
+use JMS\Serializer\Exclusion\GroupsExclusionStrategy;
+use JMS\Serializer\SerializationContext;
use Metadata\MetadataFactoryInterface;
use Nelmio\ApiDocBundle\Util\DocCommentExtractor;
use JMS\Serializer\Metadata\PropertyMetadata;
@@ -22,7 +24,6 @@ use JMS\Serializer\Naming\PropertyNamingStrategyInterface;
*/
class JmsMetadataParser implements ParserInterface
{
-
/**
* @var \Metadata\MetadataFactoryInterface
*/
@@ -54,10 +55,12 @@ class JmsMetadataParser implements ParserInterface
/**
* {@inheritdoc}
*/
- public function supports($input)
+ public function supports(array $input)
{
+ $className = $input['class'];
+
try {
- if ($meta = $this->factory->getMetadataForClass($input)) {
+ if ($meta = $this->factory->getMetadataForClass($className)) {
return true;
}
} catch (\ReflectionException $e) {
@@ -69,9 +72,12 @@ class JmsMetadataParser implements ParserInterface
/**
* {@inheritdoc}
*/
- public function parse($input)
+ public function parse(array $input)
{
- return $this->doParse($input);
+ $className = $input['class'];
+ $groups = $input['groups'];
+
+ return $this->doParse($className, array(), $groups);
}
/**
@@ -79,10 +85,11 @@ class JmsMetadataParser implements ParserInterface
*
* @param string $className Class to get all metadata for
* @param array $visited Classes we've already visited to prevent infinite recursion.
+ * @param array $groups Serialization groups to include.
* @return array metadata for given class
* @throws \InvalidArgumentException
*/
- protected function doParse($className, $visited = array())
+ protected function doParse($className, $visited = array(), array $groups = array())
{
$meta = $this->factory->getMetadataForClass($className);
@@ -90,6 +97,9 @@ class JmsMetadataParser implements ParserInterface
throw new \InvalidArgumentException(sprintf("No metadata found for class %s", $className));
}
+ $exclusionStrategies = array();
+ $exclusionStrategies[] = new GroupsExclusionStrategy($groups);
+
$params = array();
// iterate over property metadata
@@ -99,11 +109,21 @@ class JmsMetadataParser implements ParserInterface
$dataType = $this->processDataType($item);
+ // apply exclusion strategies
+ foreach ($exclusionStrategies as $strategy) {
+ if (true === $strategy->shouldSkipProperty($item, SerializationContext::create())) {
+ continue 2;
+ }
+ }
+
$params[$name] = array(
- 'dataType' => $dataType['normalized'],
- '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($className, $item),
- 'readonly' => $item->readOnly
+ 'dataType' => $dataType['normalized'],
+ '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($className, $item),
+ 'readonly' => $item->readOnly,
+ 'sinceVersion' => $item->sinceVersion,
+ 'untilVersion' => $item->untilVersion,
);
// if class already parsed, continue, to avoid infinite recursion
@@ -113,8 +133,8 @@ class JmsMetadataParser implements ParserInterface
// check for nested classes with JMS metadata
if ($dataType['class'] && null !== $this->factory->getMetadataForClass($dataType['class'])) {
- $visited[] = $dataType['class'];
- $params[$name]['children'] = $this->doParse($dataType['class'], $visited);
+ $visited[] = $dataType['class'];
+ $params[$name]['children'] = $this->doParse($dataType['class'], $visited, $groups);
}
}
}
@@ -206,5 +226,4 @@ class JmsMetadataParser implements ParserInterface
return $extracted;
}
-
}
diff --git a/Parser/ParserInterface.php b/Parser/ParserInterface.php
index fcdadf2..398b230 100644
--- a/Parser/ParserInterface.php
+++ b/Parser/ParserInterface.php
@@ -19,10 +19,10 @@ interface ParserInterface
/**
* Return true/false whether this class supports parsing the given class.
*
- * @param string $item The string type of input to parse.
+ * @param array $item containing the following fields: class, groups. Of which groups is optional
* @return boolean
*/
- public function supports($item);
+ public function supports(array $item);
/**
* Returns an array of class property metadata where each item is a key (the property name) and
@@ -37,6 +37,5 @@ interface ParserInterface
* @param string $item The string type of input to parse.
* @return array
*/
- public function parse($item);
-
+ public function parse(array $item);
}
diff --git a/README.md b/README.md
index 897fc2b..9ed3a02 100644
--- a/README.md
+++ b/README.md
@@ -105,7 +105,7 @@ The following properties are available:
* `filters`: an array of filters;
* `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)
+ metadata, useful for POST|PUT methods, either as FQCN or as form type (if it is registered in the form factory in the container).
* `output`: the output type associated with the response. Specified and parsed the same way as `input`.
@@ -183,6 +183,31 @@ Also bundle will get information from the other annotations:
Route functions marked as @deprecated will be set method as deprecation in documentation.
+#### JMS Serializer features ####
+
+The bundle has support for some of the JMS Serializer features and use these extra information in the generated documentation.
+
+##### Group Exclusion Strategy #####
+
+If your classes use [JMS Group Exclusion Strategy](http://jmsyst.com/libs/serializer/master/cookbook/exclusion_strategies#creating-different-views-of-your-objects),
+you can specify which groups to use when generating the documentation by using this syntax :
+
+ ```
+ input={
+ "class"="Acme\Bundle\Entity\User",
+ "groups"={"update", "public"}
+ }
+ ```
+
+ In this case the groups 'update' and 'public' are used.
+
+ This feature also works for the `output` property.
+
+##### Versioning Objects #####
+
+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.
+
### Documentation on-the-fly ###
By calling an URL with the parameter `?_doc=1`, you will get the corresponding documentation if available.
diff --git a/Resources/views/Components/version.html.twig b/Resources/views/Components/version.html.twig
new file mode 100644
index 0000000..24d5848
--- /dev/null
+++ b/Resources/views/Components/version.html.twig
@@ -0,0 +1,8 @@
+{% if sinceVersion is empty and untilVersion is empty %}
+*
+{% else %}
+ {% if sinceVersion is not empty %}>={{ sinceVersion }}{% endif %}
+ {% if untilVersion is not empty %}
+ {% if sinceVersion is not empty %},{% endif %}<={{ untilVersion }}
+ {% endif %}
+{% endif %}
diff --git a/Resources/views/method.html.twig b/Resources/views/method.html.twig
index 1602b68..81649a5 100644
--- a/Resources/views/method.html.twig
+++ b/Resources/views/method.html.twig
@@ -134,6 +134,7 @@
Parameter |
Type |
+ Versions |
Description |
@@ -142,6 +143,7 @@
{{ name }} |
{{ infos.dataType }} |
+ {% include 'NelmioApiDocBundle:Components:version.html.twig' with {'sinceVersion': infos.sinceVersion, 'untilVersion': infos.untilVersion} only %} |
{{ infos.description }} |
{% endfor %}
diff --git a/Tests/Extractor/ApiDocExtractorTest.php b/Tests/Extractor/ApiDocExtractorTest.php
index 8eba7ca..1948c36 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 = 19;
+ const ROUTES_QUANTITY = 20;
public function testAll()
{
@@ -66,7 +66,7 @@ class ApiDocExtractorTest extends WebTestCase
$this->assertFalse(isset($array2['filters']));
$this->assertEquals('Nelmio\ApiDocBundle\Tests\Fixtures\Form\TestType', $a2->getInput());
- $a3 = $data['12']['annotation'];
+ $a3 = $data['13']['annotation'];
$this->assertTrue($a3->getHttps());
}
@@ -155,7 +155,7 @@ class ApiDocExtractorTest extends WebTestCase
"This method is useful to test if the getDocComment works.",
$annotation->getDescription()
);
-
+
$data = $annotation->toArray();
$this->assertEquals(
4,
diff --git a/Tests/Fixtures/Controller/TestController.php b/Tests/Fixtures/Controller/TestController.php
index bcb3b0a..ea86559 100644
--- a/Tests/Fixtures/Controller/TestController.php
+++ b/Tests/Fixtures/Controller/TestController.php
@@ -172,7 +172,7 @@ class TestController
public function cachedAction()
{
}
-
+
/**
* @ApiDoc()
* @deprecated
@@ -180,4 +180,13 @@ class TestController
public function deprecatedAction()
{
}
+
+ /**
+ * @ApiDoc(
+ * output="Nelmio\ApiDocBundle\Tests\Fixtures\Model\JmsTest"
+ * )
+ */
+ public function jmsReturnNestedOutputAction()
+ {
+ }
}
diff --git a/Tests/Fixtures/Model/JmsNested.php b/Tests/Fixtures/Model/JmsNested.php
index 37162cc..18877fb 100644
--- a/Tests/Fixtures/Model/JmsNested.php
+++ b/Tests/Fixtures/Model/JmsNested.php
@@ -36,4 +36,22 @@ class JmsNested
*/
public $parent;
+ /**
+ * @Jms\Type("string")
+ * @Jms\Since("0.2")
+ */
+ public $since;
+
+ /**
+ * @Jms\Type("string")
+ * @Jms\Until("0.3")
+ */
+ public $until;
+
+ /**
+ * @Jms\Type("string")
+ * @Jms\Since("0.4")
+ * @Jms\Until("0.5")
+ */
+ public $sinceAndUntil;
}
diff --git a/Tests/Fixtures/app/AppKernel.php b/Tests/Fixtures/app/AppKernel.php
index 0ebbbb0..adc9506 100644
--- a/Tests/Fixtures/app/AppKernel.php
+++ b/Tests/Fixtures/app/AppKernel.php
@@ -12,23 +12,7 @@
namespace Nelmio\ApiDocBundle\Tests\Functional;
// get the autoload file
-$dir = __DIR__;
-$lastDir = null;
-while ($dir !== $lastDir) {
- $lastDir = $dir;
-
- if (is_file($dir.'/autoload.php')) {
- require_once $dir.'/autoload.php';
- break;
- }
-
- if (is_file($dir.'/autoload.php.dist')) {
- require_once $dir.'/autoload.php.dist';
- break;
- }
-
- $dir = dirname($dir);
-}
+require_once __DIR__.'/../../../vendor/autoload.php';
use Symfony\Component\Config\Loader\LoaderInterface;
use Symfony\Component\HttpKernel\Kernel;
diff --git a/Tests/Fixtures/app/config/routing.yml b/Tests/Fixtures/app/config/routing.yml
index 65cc687..fcbf561 100644
--- a/Tests/Fixtures/app/config/routing.yml
+++ b/Tests/Fixtures/app/config/routing.yml
@@ -117,3 +117,7 @@ test_route_17:
defaults: { _controller: NelmioApiDocTestBundle:Test:deprecated }
requirements:
_method: GET
+
+test_return_nested_output:
+ pattern: /return-nested-output
+ defaults: { _controller: NelmioApiDocTestBundle:Test:jmsReturnNestedOutput, _format: json }
diff --git a/Tests/Formatter/MarkdownFormatterTest.php b/Tests/Formatter/MarkdownFormatterTest.php
index ff62f91..12da9a5 100644
--- a/Tests/Formatter/MarkdownFormatterTest.php
+++ b/Tests/Formatter/MarkdownFormatterTest.php
@@ -244,6 +244,21 @@ nested[parent][nested_array][]:
* type: array of objects (JmsNested)
* required: false
+nested[since]:
+
+ * type: string
+ * required: false
+
+nested[until]:
+
+ * type: string
+ * required: false
+
+nested[since_and_until]:
+
+ * type: string
+ * required: false
+
nested_array[]:
* type: array of objects (JmsNested)
@@ -285,6 +300,98 @@ _This method is useful to test if the getDocComment works._
- Description: The param id
+### `ANY` /return-nested-output ###
+
+
+#### Response ####
+
+foo:
+
+ * type: string
+
+bar:
+
+ * type: DateTime
+
+number:
+
+ * type: double
+
+arr:
+
+ * type: array
+
+nested:
+
+ * type: object (JmsNested)
+
+nested[foo]:
+
+ * type: DateTime
+
+nested[bar]:
+
+ * type: string
+
+nested[baz][]:
+
+ * type: array of integers
+ * description: Epic description.
+
+With multiple lines.
+
+nested[circular]:
+
+ * type: object (JmsNested)
+
+nested[parent]:
+
+ * type: object (JmsTest)
+
+nested[parent][foo]:
+
+ * type: string
+
+nested[parent][bar]:
+
+ * type: DateTime
+
+nested[parent][number]:
+
+ * type: double
+
+nested[parent][arr]:
+
+ * type: array
+
+nested[parent][nested]:
+
+ * type: object (JmsNested)
+
+nested[parent][nested_array][]:
+
+ * type: array of objects (JmsNested)
+
+nested[since]:
+
+ * type: string
+ * versions: >=0.2
+
+nested[until]:
+
+ * type: string
+ * versions: <=0.3
+
+nested[since_and_until]:
+
+ * type: string
+ * versions: >=0.4,<=0.5
+
+nested_array[]:
+
+ * type: array of objects (JmsNested)
+
+
### `ANY` /secure-route ###
diff --git a/Tests/Formatter/SimpleFormatterTest.php b/Tests/Formatter/SimpleFormatterTest.php
index 3a640a2..603a05e 100644
--- a/Tests/Formatter/SimpleFormatterTest.php
+++ b/Tests/Formatter/SimpleFormatterTest.php
@@ -252,6 +252,8 @@ class SimpleFormatterTest extends WebTestCase
'required' => false,
'description' => '',
'readonly' => false,
+ 'sinceVersion' => null,
+ 'untilVersion' => null,
),
'bar' =>
array(
@@ -259,6 +261,8 @@ class SimpleFormatterTest extends WebTestCase
'required' => false,
'description' => '',
'readonly' => true,
+ 'sinceVersion' => null,
+ 'untilVersion' => null,
),
'number' =>
array(
@@ -266,6 +270,8 @@ class SimpleFormatterTest extends WebTestCase
'required' => false,
'description' => '',
'readonly' => false,
+ 'sinceVersion' => null,
+ 'untilVersion' => null,
),
'arr' =>
array(
@@ -273,6 +279,8 @@ class SimpleFormatterTest extends WebTestCase
'required' => false,
'description' => '',
'readonly' => false,
+ 'sinceVersion' => null,
+ 'untilVersion' => null,
),
'nested' =>
array(
@@ -280,6 +288,8 @@ class SimpleFormatterTest extends WebTestCase
'required' => false,
'description' => '',
'readonly' => false,
+ 'sinceVersion' => null,
+ 'untilVersion' => null,
'children' =>
array(
'foo' =>
@@ -288,6 +298,8 @@ class SimpleFormatterTest extends WebTestCase
'required' => false,
'description' => '',
'readonly' => true,
+ 'sinceVersion' => null,
+ 'untilVersion' => null,
),
'bar' =>
array(
@@ -295,6 +307,8 @@ class SimpleFormatterTest extends WebTestCase
'required' => false,
'description' => '',
'readonly' => false,
+ 'sinceVersion' => null,
+ 'untilVersion' => null,
),
'baz' =>
array(
@@ -304,6 +318,8 @@ class SimpleFormatterTest extends WebTestCase
With multiple lines.',
'readonly' => false,
+ 'sinceVersion' => null,
+ 'untilVersion' => null,
),
'circular' =>
array(
@@ -311,6 +327,8 @@ With multiple lines.',
'required' => false,
'description' => '',
'readonly' => false,
+ 'sinceVersion' => null,
+ 'untilVersion' => null,
),
'parent' =>
array(
@@ -318,6 +336,8 @@ With multiple lines.',
'required' => false,
'description' => '',
'readonly' => false,
+ 'sinceVersion' => null,
+ 'untilVersion' => null,
'children' =>
array(
'foo' =>
@@ -326,6 +346,8 @@ With multiple lines.',
'required' => false,
'description' => '',
'readonly' => false,
+ 'sinceVersion' => null,
+ 'untilVersion' => null,
),
'bar' =>
array(
@@ -333,6 +355,8 @@ With multiple lines.',
'required' => false,
'description' => '',
'readonly' => true,
+ 'sinceVersion' => null,
+ 'untilVersion' => null,
),
'number' =>
array(
@@ -340,6 +364,8 @@ With multiple lines.',
'required' => false,
'description' => '',
'readonly' => false,
+ 'sinceVersion' => null,
+ 'untilVersion' => null,
),
'arr' =>
array(
@@ -347,6 +373,8 @@ With multiple lines.',
'required' => false,
'description' => '',
'readonly' => false,
+ 'sinceVersion' => null,
+ 'untilVersion' => null,
),
'nested' =>
array(
@@ -354,6 +382,8 @@ With multiple lines.',
'required' => false,
'description' => '',
'readonly' => false,
+ 'sinceVersion' => null,
+ 'untilVersion' => null,
),
'nested_array' =>
array(
@@ -361,9 +391,38 @@ With multiple lines.',
'required' => false,
'description' => '',
'readonly' => false,
+ 'sinceVersion' => null,
+ 'untilVersion' => null,
),
),
),
+ 'since' =>
+ array (
+ 'dataType' => 'string',
+ 'required' => false,
+ 'description' => '',
+ 'readonly' => false,
+ 'sinceVersion' => '0.2',
+ 'untilVersion' => null,
+ ),
+ 'until' =>
+ array (
+ 'dataType' => 'string',
+ 'required' => false,
+ 'description' => '',
+ 'readonly' => false,
+ 'sinceVersion' => null,
+ 'untilVersion' => '0.3',
+ ),
+ 'since_and_until' =>
+ array (
+ 'dataType' => 'string',
+ 'required' => false,
+ 'description' => '',
+ 'readonly' => false,
+ 'sinceVersion' => '0.4',
+ 'untilVersion' => '0.5',
+ ),
),
),
'nested_array' =>
@@ -372,6 +431,8 @@ With multiple lines.',
'required' => false,
'description' => '',
'readonly' => false,
+ 'sinceVersion' => null,
+ 'untilVersion' => null,
),
),
'https' => false,
@@ -438,6 +499,205 @@ And, it supports multilines until the first \'@\' char.',
'deprecated' => false,
),
7 =>
+ array(
+ 'method' => 'ANY',
+ 'uri' => '/return-nested-output',
+ 'https' => false,
+ 'authentication' => false,
+ 'deprecated' => false,
+ 'response' =>
+ array (
+ 'foo' =>
+ array (
+ 'dataType' => 'string',
+ 'required' => false,
+ 'description' => '',
+ 'readonly' => false,
+ 'sinceVersion' => null,
+ 'untilVersion' => null,
+ ),
+ 'bar' =>
+ array (
+ 'dataType' => 'DateTime',
+ 'required' => false,
+ 'description' => '',
+ 'readonly' => true,
+ 'sinceVersion' => null,
+ 'untilVersion' => null,
+ ),
+ 'number' =>
+ array (
+ 'dataType' => 'double',
+ 'required' => false,
+ 'description' => '',
+ 'readonly' => false,
+ 'sinceVersion' => null,
+ 'untilVersion' => null,
+ ),
+ 'arr' =>
+ array (
+ 'dataType' => 'array',
+ 'required' => false,
+ 'description' => '',
+ 'readonly' => false,
+ 'sinceVersion' => null,
+ 'untilVersion' => null,
+ ),
+ 'nested' =>
+ array (
+ 'dataType' => 'object (JmsNested)',
+ 'required' => false,
+ 'description' => '',
+ 'readonly' => false,
+ 'sinceVersion' => null,
+ 'untilVersion' => null,
+ 'children' =>
+ array (
+ 'foo' =>
+ array (
+ 'dataType' => 'DateTime',
+ 'required' => false,
+ 'description' => '',
+ 'readonly' => true,
+ 'sinceVersion' => null,
+ 'untilVersion' => null,
+ ),
+ 'bar' =>
+ array (
+ 'dataType' => 'string',
+ 'required' => false,
+ 'description' => '',
+ 'readonly' => false,
+ 'sinceVersion' => null,
+ 'untilVersion' => null,
+ ),
+ 'baz' =>
+ array (
+ 'dataType' => 'array of integers',
+ 'required' => false,
+ 'description' => 'Epic description.
+
+With multiple lines.',
+ 'readonly' => false,
+ 'sinceVersion' => null,
+ 'untilVersion' => null,
+ ),
+ 'circular' =>
+ array (
+ 'dataType' => 'object (JmsNested)',
+ 'required' => false,
+ 'description' => '',
+ 'readonly' => false,
+ 'sinceVersion' => null,
+ 'untilVersion' => null,
+ ),
+ 'parent' =>
+ array (
+ 'dataType' => 'object (JmsTest)',
+ 'required' => false,
+ 'description' => '',
+ 'readonly' => false,
+ 'sinceVersion' => null,
+ 'untilVersion' => null,
+ 'children' =>
+ array (
+ 'foo' =>
+ array (
+ 'dataType' => 'string',
+ 'required' => false,
+ 'description' => '',
+ 'readonly' => false,
+ 'sinceVersion' => null,
+ 'untilVersion' => null,
+ ),
+ 'bar' =>
+ array (
+ 'dataType' => 'DateTime',
+ 'required' => false,
+ 'description' => '',
+ 'readonly' => true,
+ 'sinceVersion' => null,
+ 'untilVersion' => null,
+ ),
+ 'number' =>
+ array (
+ 'dataType' => 'double',
+ 'required' => false,
+ 'description' => '',
+ 'readonly' => false,
+ 'sinceVersion' => null,
+ 'untilVersion' => null,
+ ),
+ 'arr' =>
+ array (
+ 'dataType' => 'array',
+ 'required' => false,
+ 'description' => '',
+ 'readonly' => false,
+ 'sinceVersion' => null,
+ 'untilVersion' => null,
+ ),
+ 'nested' =>
+ array (
+ 'dataType' => 'object (JmsNested)',
+ 'required' => false,
+ 'description' => '',
+ 'readonly' => false,
+ 'sinceVersion' => null,
+ 'untilVersion' => null,
+ ),
+ 'nested_array' =>
+ array (
+ 'dataType' => 'array of objects (JmsNested)',
+ 'required' => false,
+ 'description' => '',
+ 'readonly' => false,
+ 'sinceVersion' => null,
+ 'untilVersion' => null,
+ ),
+ ),
+ ),
+ 'since' =>
+ array (
+ 'dataType' => 'string',
+ 'required' => false,
+ 'description' => '',
+ 'readonly' => false,
+ 'sinceVersion' => '0.2',
+ 'untilVersion' => null,
+ ),
+ 'until' =>
+ array (
+ 'dataType' => 'string',
+ 'required' => false,
+ 'description' => '',
+ 'readonly' => false,
+ 'sinceVersion' => null,
+ 'untilVersion' => '0.3',
+ ),
+ 'since_and_until' =>
+ array (
+ 'dataType' => 'string',
+ 'required' => false,
+ 'description' => '',
+ 'readonly' => false,
+ 'sinceVersion' => '0.4',
+ 'untilVersion' => '0.5',
+ ),
+ ),
+ ),
+ 'nested_array' =>
+ array (
+ 'dataType' => 'array of objects (JmsNested)',
+ 'required' => false,
+ 'description' => '',
+ 'readonly' => false,
+ 'sinceVersion' => null,
+ 'untilVersion' => null,
+ ),
+ ),
+ ),
+ 8 =>
array(
'method' => 'ANY',
'uri' => '/secure-route',
@@ -454,7 +714,7 @@ And, it supports multilines until the first \'@\' char.',
'authentication' => false,
'deprecated' => false,
),
- 8 =>
+ 9 =>
array(
'method' => 'ANY',
'uri' => '/yet-another/{id}',
@@ -471,7 +731,7 @@ And, it supports multilines until the first \'@\' char.',
'authentication' => false,
'deprecated' => false,
),
- 9 =>
+ 10 =>
array(
'method' => 'GET',
'uri' => '/z-action-with-deprecated-indicator',
@@ -479,7 +739,7 @@ And, it supports multilines until the first \'@\' char.',
'authentication' => false,
'deprecated' => true,
),
- 10 =>
+ 11 =>
array(
'method' => 'GET',
'uri' => '/z-action-with-query-param',
@@ -496,7 +756,7 @@ And, it supports multilines until the first \'@\' char.',
'authentication' => false,
'deprecated' => false,
),
- 11 =>
+ 12 =>
array(
'method' => 'GET',
'uri' => '/z-action-with-query-param-no-default',
@@ -512,7 +772,7 @@ And, it supports multilines until the first \'@\' char.',
'authentication' => false,
'deprecated' => false,
),
- 12 =>
+ 13 =>
array(
'method' => 'GET',
'uri' => '/z-action-with-query-param-strict',
@@ -529,7 +789,7 @@ And, it supports multilines until the first \'@\' char.',
'authentication' => false,
'deprecated' => false,
),
- 13 =>
+ 14 =>
array(
'method' => 'POST',
'uri' => '/z-action-with-request-param',
diff --git a/Tests/Parser/JmsMetadataParserTest.php b/Tests/Parser/JmsMetadataParserTest.php
index dd30b75..3e41883 100644
--- a/Tests/Parser/JmsMetadataParserTest.php
+++ b/Tests/Parser/JmsMetadataParserTest.php
@@ -5,6 +5,7 @@ use Nelmio\ApiDocBundle\Tests\Fixtures\Model\JmsNested;
use Nelmio\ApiDocBundle\Parser\JmsMetadataParser;
use JMS\Serializer\Metadata\ClassMetadata;
use JMS\Serializer\Metadata\PropertyMetadata;
+use JMS\Serializer\Naming\CamelCaseNamingStrategy;
class JmsMetadataParserTest extends \PHPUnit_Framework_TestCase
{
@@ -61,7 +62,7 @@ class JmsMetadataParserTest extends \PHPUnit_Framework_TestCase
->method('translateName')
->will($this->returnValue('baz'));
- $input = new JmsNested();
+ $input = 'Nelmio\ApiDocBundle\Tests\Fixtures\Model\JmsNested';
$metadataFactory->expects($this->once())
->method('getMetadataForClass')
@@ -70,28 +71,278 @@ class JmsMetadataParserTest extends \PHPUnit_Framework_TestCase
$jmsMetadataParser = new JmsMetadataParser($metadataFactory, $propertyNamingStrategy, $docCommentExtractor);
- $output = $jmsMetadataParser->parse($input);
-
- $this->assertEquals(array(
- 'foo' => array(
- 'dataType' => 'DateTime',
- 'required' => false,
- 'description' => '',
- 'readonly' => false
- ),
- 'bar' => array(
- 'dataType' => 'string',
- 'required' => false,
- 'description' => '',
- 'readonly' => false
- ),
- 'baz' => array(
- 'dataType' => 'array of integers',
- 'required' => false,
- 'description' => '',
- 'readonly' => false
+ $output = $jmsMetadataParser->parse(
+ array(
+ 'class' => $input,
+ 'groups' => array(),
)
- ), $output);
+ );
+
+ $this->assertEquals(
+ array(
+ 'foo' => array(
+ 'dataType' => 'DateTime',
+ 'required' => false,
+ 'description' => null,
+ 'readonly' => false,
+ 'sinceVersion' => null,
+ 'untilVersion' => null,
+ ),
+ 'bar' => array(
+ 'dataType' => 'string',
+ 'required' => false,
+ 'description' => null,
+ 'readonly' => false,
+ 'sinceVersion' => null,
+ 'untilVersion' => null,
+ ),
+ 'baz' => array(
+ 'dataType' => 'array of integers',
+ 'required' => false,
+ 'description' => null,
+ 'readonly' => false,
+ 'sinceVersion' => null,
+ 'untilVersion' => null,
+ )
+ ),
+ $output
+ );
+ }
+
+ public function testParserWithGroups()
+ {
+ $metadataFactory = $this->getMock('Metadata\MetadataFactoryInterface');
+ $docCommentExtractor = $this->getMockBuilder('Nelmio\ApiDocBundle\Util\DocCommentExtractor')
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $propertyMetadataFoo = new PropertyMetadata('Nelmio\ApiDocBundle\Tests\Fixtures\Model\JmsNested', 'foo');
+ $propertyMetadataFoo->type = array('name' => 'string');
+
+ $propertyMetadataBar = new PropertyMetadata('Nelmio\ApiDocBundle\Tests\Fixtures\Model\JmsNested', 'bar');
+ $propertyMetadataBar->type = array('name' => 'string');
+ $propertyMetadataBar->groups = array('Default', 'Special');
+
+ $propertyMetadataBaz = new PropertyMetadata('Nelmio\ApiDocBundle\Tests\Fixtures\Model\JmsNested', 'baz');
+ $propertyMetadataBaz->type = array('name' => 'string');
+ $propertyMetadataBaz->groups = array('Special');
+
+ $input = 'Nelmio\ApiDocBundle\Tests\Fixtures\Model\JmsNested';
+
+ $metadata = new ClassMetadata($input);
+ $metadata->addPropertyMetadata($propertyMetadataFoo);
+ $metadata->addPropertyMetadata($propertyMetadataBar);
+ $metadata->addPropertyMetadata($propertyMetadataBaz);
+
+ $metadataFactory->expects($this->any())
+ ->method('getMetadataForClass')
+ ->with($input)
+ ->will($this->returnValue($metadata));
+
+ $propertyNamingStrategy = new CamelCaseNamingStrategy();
+
+ $jmsMetadataParser = new JmsMetadataParser($metadataFactory, $propertyNamingStrategy, $docCommentExtractor);
+
+ // No group specified.
+ $output = $jmsMetadataParser->parse(
+ array(
+ 'class' => $input,
+ 'groups' => array(),
+ )
+ );
+
+ $this->assertEquals(
+ array(
+ 'foo' => array(
+ 'dataType' => 'string',
+ 'required' => false,
+ 'description' => null,
+ 'readonly' => false,
+ 'sinceVersion' => null,
+ 'untilVersion' => null,
+ ),
+ 'bar' => array(
+ 'dataType' => 'string',
+ 'required' => false,
+ 'description' => null,
+ 'readonly' => false,
+ 'sinceVersion' => null,
+ 'untilVersion' => null,
+ ),
+ ),
+ $output
+ );
+
+ // Default group.
+ $output = $jmsMetadataParser->parse(
+ array(
+ 'class' => $input,
+ 'groups' => array('Default'),
+ )
+ );
+
+ $this->assertEquals(
+ array(
+ 'foo' => array(
+ 'dataType' => 'string',
+ 'required' => false,
+ 'description' => null,
+ 'readonly' => false,
+ 'sinceVersion' => null,
+ 'untilVersion' => null,
+ ),
+ 'bar' => array(
+ 'dataType' => 'string',
+ 'required' => false,
+ 'description' => null,
+ 'readonly' => false,
+ 'sinceVersion' => null,
+ 'untilVersion' => null,
+ ),
+ ),
+ $output
+ );
+
+ // Special group.
+ $output = $jmsMetadataParser->parse(
+ array(
+ 'class' => $input,
+ 'groups' => array('Special'),
+ )
+ );
+
+ $this->assertEquals(
+ array(
+ 'bar' => array(
+ 'dataType' => 'string',
+ 'required' => false,
+ 'description' => null,
+ 'readonly' => false,
+ 'sinceVersion' => null,
+ 'untilVersion' => null,
+ ),
+ 'baz' => array(
+ 'dataType' => 'string',
+ 'required' => false,
+ 'description' => null,
+ 'readonly' => false,
+ 'sinceVersion' => null,
+ 'untilVersion' => null,
+ ),
+ ),
+ $output
+ );
+
+ // Default + Special groups.
+ $output = $jmsMetadataParser->parse(
+ array(
+ 'class' => $input,
+ 'groups' => array('Default', 'Special'),
+ )
+ );
+
+ $this->assertEquals(
+ array(
+ 'foo' => array(
+ 'dataType' => 'string',
+ 'required' => false,
+ 'description' => null,
+ 'readonly' => false,
+ 'sinceVersion' => null,
+ 'untilVersion' => null,
+ ),
+ 'bar' => array(
+ 'dataType' => 'string',
+ 'required' => false,
+ 'description' => null,
+ 'readonly' => false,
+ 'sinceVersion' => null,
+ 'untilVersion' => null,
+ ),
+ 'baz' => array(
+ 'dataType' => 'string',
+ 'required' => false,
+ 'description' => null,
+ 'readonly' => false,
+ 'sinceVersion' => null,
+ 'untilVersion' => null,
+ )
+ ),
+ $output
+ );
+ }
+
+ public function testParserWithVersion()
+ {
+ $metadataFactory = $this->getMock('Metadata\MetadataFactoryInterface');
+ $docCommentExtractor = $this->getMockBuilder('Nelmio\ApiDocBundle\Util\DocCommentExtractor')
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $propertyMetadataFoo = new PropertyMetadata('Nelmio\ApiDocBundle\Tests\Fixtures\Model\JmsNested', 'foo');
+ $propertyMetadataFoo->type = array('name' => 'string');
+
+ $propertyMetadataBar = new PropertyMetadata('Nelmio\ApiDocBundle\Tests\Fixtures\Model\JmsNested', 'bar');
+ $propertyMetadataBar->type = array('name' => 'string');
+ $propertyMetadataBar->sinceVersion = '2.0';
+
+ $propertyMetadataBaz = new PropertyMetadata('Nelmio\ApiDocBundle\Tests\Fixtures\Model\JmsNested', 'baz');
+ $propertyMetadataBaz->type = array('name' => 'string');
+ $propertyMetadataBaz->untilVersion = '3.0';
+
+ $input = 'Nelmio\ApiDocBundle\Tests\Fixtures\Model\JmsNested';
+
+ $metadata = new ClassMetadata($input);
+ $metadata->addPropertyMetadata($propertyMetadataFoo);
+ $metadata->addPropertyMetadata($propertyMetadataBar);
+ $metadata->addPropertyMetadata($propertyMetadataBaz);
+
+ $metadataFactory->expects($this->any())
+ ->method('getMetadataForClass')
+ ->with($input)
+ ->will($this->returnValue($metadata));
+
+ $propertyNamingStrategy = new CamelCaseNamingStrategy();
+
+ $jmsMetadataParser = new JmsMetadataParser($metadataFactory, $propertyNamingStrategy, $docCommentExtractor);
+
+ // No group specified.
+ $output = $jmsMetadataParser->parse(
+ array(
+ 'class' => $input,
+ 'groups' => array(),
+ )
+ );
+
+ $this->assertEquals(
+ array(
+ 'foo' => array(
+ 'dataType' => 'string',
+ 'required' => false,
+ 'description' => null,
+ 'readonly' => false,
+ 'sinceVersion' => null,
+ 'untilVersion' => null,
+ ),
+ 'bar' => array(
+ 'dataType' => 'string',
+ 'required' => false,
+ 'description' => null,
+ 'readonly' => false,
+ 'sinceVersion' => '2.0',
+ 'untilVersion' => null,
+ ),
+ 'baz' => array(
+ 'dataType' => 'string',
+ 'required' => false,
+ 'description' => null,
+ 'readonly' => false,
+ 'sinceVersion' => null,
+ 'untilVersion' => '3.0',
+ )
+ ),
+ $output
+ );
}
public function dataTestParserWithNestedType()
diff --git a/composer.json b/composer.json
index 7d03918..76b1397 100644
--- a/composer.json
+++ b/composer.json
@@ -21,6 +21,7 @@
"dflydev/markdown": "1.0.*"
},
"conflict": {
+ "jms/serializer": "<0.12",
"jms/serializer-bundle": "<0.11"
},
"require-dev": {