From 06cfe9d48b5ff14f5e290927f48754f6e462aaac Mon Sep 17 00:00:00 2001 From: Bez Hermoso Date: Mon, 28 Jul 2014 13:14:59 -0700 Subject: [PATCH 1/7] Allow parsers to remove/replace root parameters. --- Extractor/ApiDocExtractor.php | 11 ++++++++++- Resources/config/services.xml | 6 ++++++ 2 files changed, 16 insertions(+), 1 deletion(-) diff --git a/Extractor/ApiDocExtractor.php b/Extractor/ApiDocExtractor.php index 133a2d5..445b3ed 100644 --- a/Extractor/ApiDocExtractor.php +++ b/Extractor/ApiDocExtractor.php @@ -390,6 +390,9 @@ class ApiDocExtractor * - Array parameters are recursively merged. * - Non-null default values prevail over null default values. Later values overrides previous defaults. * + * However, if newly-returned parameter array contains a parameter with NULL, the parameter is removed from the merged results. + * If the parameter is not present in the newly-returned array, then it is left as-is. + * * @param array $p1 The pre-existing parameters array. * @param array $p2 The newly-returned parameters array. * @return array The resulting, merged array. @@ -399,9 +402,15 @@ class ApiDocExtractor $params = $p1; foreach ($p2 as $propname => $propvalue) { + + if ($propvalue === null) { + unset($params[$propname]); + continue; + } + if (!isset($p1[$propname])) { $params[$propname] = $propvalue; - } else { + } elseif (is_array($propvalue)) { $v1 = $p1[$propname]; foreach ($propvalue as $name => $value) { diff --git a/Resources/config/services.xml b/Resources/config/services.xml index da00c21..dbd694b 100644 --- a/Resources/config/services.xml +++ b/Resources/config/services.xml @@ -13,6 +13,8 @@ Nelmio\ApiDocBundle\Extractor\Handler\JmsSecurityExtraHandler Nelmio\ApiDocBundle\Extractor\Handler\SensioFrameworkExtraHandler Nelmio\ApiDocBundle\Extractor\Handler\PhpDocHandler + + Nelmio\ApiDocBundle\Parser\CollectionParser @@ -52,6 +54,10 @@ + + + + From f5c1b06807f79b1948803a1497284dc0872bf39b Mon Sep 17 00:00:00 2001 From: Bez Hermoso Date: Thu, 7 Aug 2014 17:02:15 -0700 Subject: [PATCH 2/7] Support for collections. --- Extractor/ApiDocExtractor.php | 9 + Parser/CollectionParser.php | 77 +++++ .../Controller/ResourceController.php | 7 +- Tests/Formatter/MarkdownFormatterTest.php | 92 ++++++ Tests/Formatter/SimpleFormatterTest.php | 263 ++++++++++++++++++ Tests/Formatter/SwaggerFormatterTest.php | 90 +++++- 6 files changed, 534 insertions(+), 4 deletions(-) create mode 100644 Parser/CollectionParser.php diff --git a/Extractor/ApiDocExtractor.php b/Extractor/ApiDocExtractor.php index 445b3ed..ed05a3c 100644 --- a/Extractor/ApiDocExtractor.php +++ b/Extractor/ApiDocExtractor.php @@ -373,6 +373,15 @@ class ApiDocExtractor $input = array('class' => $input); } + $collectionData = array(); + preg_match_all("/array<(.*)>( as (.*))?/", $input['class'], $collectionData); + + if (count($collectionData[0]) > 0) { + $input['class'] = $collectionData[1][0]; + $input['collection'] = true; + $input['collectionName'] = $collectionData[3][0]; + } + // normalize groups if (isset($input['groups']) && is_string($input['groups'])) { $input['groups'] = array_map('trim', explode(',', $input['groups'])); diff --git a/Parser/CollectionParser.php b/Parser/CollectionParser.php new file mode 100644 index 0000000..0ef9c66 --- /dev/null +++ b/Parser/CollectionParser.php @@ -0,0 +1,77 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Nelmio\ApiDocBundle\Parser; + +use Nelmio\ApiDocBundle\DataTypes; + +/** + * Handles models that are specified as collections. + * + * @author Bez Hermoso + */ +class CollectionParser implements ParserInterface, PostParserInterface +{ + + /** + * Return true/false whether this class supports parsing the given class. + * + * @param array $item containing the following fields: class, groups. Of which groups is optional + * + * @return boolean + */ + public function supports(array $item) + { + return isset($item['collection']) && $item['collection'] === true; + } + + /** + * This doesn't parse anything at this stage. + * + * @param array $item + * + * @return array + */ + public function parse(array $item) + { + return array(); + } + + /** + * @param array|string $item The string type of input to parse. + * @param array $parameters The previously-parsed parameters array. + * + * @return array + */ + public function postParse(array $item, array $parameters) + { + $origParameters = $parameters; + + foreach ($parameters as $name => $body) { + $parameters[$name] = null; + } + + $collectionName = isset($item['collectionName']) ? $item['collectionName'] : ''; + + $parameters[$collectionName] = array( + 'dataType' => null, // Delegates to ApiDocExtractor#generateHumanReadableTypes + 'subType' => $item['class'], + 'actualType' => DataTypes::COLLECTION, + 'readonly' => true, + 'required' => true, + 'default' => true, + 'description' => '', + 'children' => $origParameters, + ); + + return $parameters; + } +} \ No newline at end of file diff --git a/Tests/Fixtures/Controller/ResourceController.php b/Tests/Fixtures/Controller/ResourceController.php index 502f9c2..74aad93 100644 --- a/Tests/Fixtures/Controller/ResourceController.php +++ b/Tests/Fixtures/Controller/ResourceController.php @@ -56,7 +56,12 @@ class ResourceController } /** - * @ApiDoc(resource=true, description="List another resource.", resourceDescription="Operations on another resource.") + * @ApiDoc( + * resource=true, + * description="List another resource.", + * resourceDescription="Operations on another resource.", + * output="array" + * ) */ public function listAnotherResourcesAction() { diff --git a/Tests/Formatter/MarkdownFormatterTest.php b/Tests/Formatter/MarkdownFormatterTest.php index c86379e..bbf59f1 100644 --- a/Tests/Formatter/MarkdownFormatterTest.php +++ b/Tests/Formatter/MarkdownFormatterTest.php @@ -38,6 +38,98 @@ _List another resource._ - Requirement: json|xml|html +#### Response #### + +[]: + + * type: array of objects (JmsTest) + +[][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) + ### `PUT|PATCH` /api/other-resources/{id}.{_format} ### diff --git a/Tests/Formatter/SimpleFormatterTest.php b/Tests/Formatter/SimpleFormatterTest.php index 5fa2734..f9bcf3d 100644 --- a/Tests/Formatter/SimpleFormatterTest.php +++ b/Tests/Formatter/SimpleFormatterTest.php @@ -1333,6 +1333,269 @@ With multiple lines.', 'authenticationRoles' => array(), 'deprecated' => false, + 'response' => array( + '' => + array( + 'dataType' => 'array of objects (JmsTest)', + 'subType' => 'Nelmio\\ApiDocBundle\\Tests\\Fixtures\\Model\\JmsTest', + 'actualType' => 'collection', + 'readonly' => true, + 'required' => true, + 'default' => true, + 'description' => '', + 'children' => + array( + 'foo' => + array( + 'dataType' => 'string', + 'actualType' => 'string', + 'subType' => null, + 'required' => false, + 'default' => null, + 'description' => '', + 'readonly' => false, + 'sinceVersion' => null, + 'untilVersion' => null, + ), + 'bar' => + array( + 'dataType' => 'DateTime', + 'actualType' => 'datetime', + 'subType' => null, + 'required' => false, + 'default' => null, + 'description' => '', + 'readonly' => true, + 'sinceVersion' => null, + 'untilVersion' => null, + ), + 'number' => + array( + 'dataType' => 'double', + 'actualType' => 'float', + 'subType' => null, + 'required' => false, + 'default' => null, + 'description' => '', + 'readonly' => false, + 'sinceVersion' => null, + 'untilVersion' => null, + ), + 'arr' => + array( + 'dataType' => 'array', + 'actualType' => 'collection', + 'subType' => null, + 'required' => false, + 'default' => null, + 'description' => '', + 'readonly' => false, + 'sinceVersion' => null, + 'untilVersion' => null, + ), + 'nested' => + array( + 'dataType' => 'object (JmsNested)', + 'actualType' => 'model', + 'subType' => 'Nelmio\\ApiDocBundle\\Tests\\Fixtures\\Model\\JmsNested', + 'required' => false, + 'default' => null, + 'description' => '', + 'readonly' => false, + 'sinceVersion' => null, + 'untilVersion' => null, + 'children' => + array( + 'foo' => + array( + 'dataType' => 'DateTime', + 'actualType' => 'datetime', + 'subType' => null, + 'required' => false, + 'default' => null, + 'description' => '', + 'readonly' => true, + 'sinceVersion' => null, + 'untilVersion' => null, + ), + 'bar' => + array( + 'dataType' => 'string', + 'actualType' => 'string', + 'subType' => null, + 'required' => false, + 'default' => 'baz', + 'description' => '', + 'readonly' => false, + 'sinceVersion' => null, + 'untilVersion' => null, + ), + 'baz' => + array( + 'dataType' => 'array of integers', + 'actualType' => 'collection', + 'subType' => 'integer', + 'required' => false, + 'default' => null, + 'description' => 'Epic description. + +With multiple lines.', + 'readonly' => false, + 'sinceVersion' => null, + 'untilVersion' => null, + ), + 'circular' => + array( + 'dataType' => 'object (JmsNested)', + 'actualType' => 'model', + 'subType' => 'Nelmio\\ApiDocBundle\\Tests\\Fixtures\\Model\\JmsNested', + 'required' => false, + 'default' => null, + 'description' => '', + 'readonly' => false, + 'sinceVersion' => null, + 'untilVersion' => null, + ), + 'parent' => + array( + 'dataType' => 'object (JmsTest)', + 'actualType' => 'model', + 'subType' => 'Nelmio\\ApiDocBundle\\Tests\\Fixtures\\Model\\JmsTest', + 'required' => false, + 'default' => null, + 'description' => '', + 'readonly' => false, + 'sinceVersion' => null, + 'untilVersion' => null, + 'children' => + array( + 'foo' => + array( + 'dataType' => 'string', + 'actualType' => 'string', + 'subType' => null, + 'required' => false, + 'default' => null, + 'description' => '', + 'readonly' => false, + 'sinceVersion' => null, + 'untilVersion' => null, + ), + 'bar' => + array( + 'dataType' => 'DateTime', + 'actualType' => 'datetime', + 'subType' => null, + 'required' => false, + 'default' => null, + 'description' => '', + 'readonly' => true, + 'sinceVersion' => null, + 'untilVersion' => null, + ), + 'number' => + array( + 'dataType' => 'double', + 'actualType' => 'float', + 'subType' => null, + 'required' => false, + 'default' => null, + 'description' => '', + 'readonly' => false, + 'sinceVersion' => null, + 'untilVersion' => null, + ), + 'arr' => + array( + 'dataType' => 'array', + 'actualType' => 'collection', + 'subType' => null, + 'required' => false, + 'default' => null, + 'description' => '', + 'readonly' => false, + 'sinceVersion' => null, + 'untilVersion' => null, + ), + 'nested' => + array( + 'dataType' => 'object (JmsNested)', + 'actualType' => 'model', + 'subType' => 'Nelmio\\ApiDocBundle\\Tests\\Fixtures\\Model\\JmsNested', + 'required' => false, + 'default' => null, + 'description' => '', + 'readonly' => false, + 'sinceVersion' => null, + 'untilVersion' => null, + ), + 'nested_array' => + array( + 'dataType' => 'array of objects (JmsNested)', + 'actualType' => 'collection', + 'subType' => 'Nelmio\\ApiDocBundle\\Tests\\Fixtures\\Model\\JmsNested', + 'required' => false, + 'default' => null, + 'description' => '', + 'readonly' => false, + 'sinceVersion' => null, + 'untilVersion' => null, + ), + ), + ), + 'since' => + array( + 'dataType' => 'string', + 'actualType' => 'string', + 'subType' => null, + 'required' => false, + 'default' => null, + 'description' => '', + 'readonly' => false, + 'sinceVersion' => '0.2', + 'untilVersion' => null, + ), + 'until' => + array( + 'dataType' => 'string', + 'actualType' => 'string', + 'subType' => null, + 'required' => false, + 'default' => null, + 'description' => '', + 'readonly' => false, + 'sinceVersion' => null, + 'untilVersion' => '0.3', + ), + 'since_and_until' => + array( + 'dataType' => 'string', + 'actualType' => 'string', + 'subType' => null, + 'required' => false, + 'default' => null, + 'description' => '', + 'readonly' => false, + 'sinceVersion' => '0.4', + 'untilVersion' => '0.5', + ), + ), + ), + 'nested_array' => + array( + 'dataType' => 'array of objects (JmsNested)', + 'actualType' => 'collection', + 'subType' => 'Nelmio\\ApiDocBundle\\Tests\\Fixtures\\Model\\JmsNested', + 'required' => false, + 'default' => null, + 'description' => '', + 'readonly' => false, + 'sinceVersion' => null, + 'untilVersion' => null, + ), + ), + ), + ), ), array( 'method' => 'PUT|PATCH', diff --git a/Tests/Formatter/SwaggerFormatterTest.php b/Tests/Formatter/SwaggerFormatterTest.php index cc79cb4..10d83c7 100644 --- a/Tests/Formatter/SwaggerFormatterTest.php +++ b/Tests/Formatter/SwaggerFormatterTest.php @@ -47,7 +47,6 @@ class SwaggerFormatterTest extends WebTestCase $actual = $this->formatter->format($data, null); - $expected = array( 'swaggerVersion' => '1.2', 'apiVersion' => '3.14', @@ -453,6 +452,7 @@ With multiple lines.', 'method' => 'GET', 'summary' => 'List another resource.', 'nickname' => 'get_other-resources', + 'type' => 'Nelmio.ApiDocBundle.Tests.Fixtures.Model.JmsTest', 'parameters' => array( @@ -470,7 +470,13 @@ With multiple lines.', ), ), 'responseMessages' => - array(), + array( + array( + 'code' => 200, + 'message' => 'See standard HTTP status code reason for 200', + 'responseModel' => 'Nelmio.ApiDocBundle.Tests.Fixtures.Model.JmsTest', + ), + ), ), ), ), @@ -545,7 +551,85 @@ With multiple lines.', ), ), 'models' => - array(), + array ( + 'Nelmio.ApiDocBundle.Tests.Fixtures.Model.JmsTest' => + array ( + 'id' => 'Nelmio.ApiDocBundle.Tests.Fixtures.Model.JmsTest', + 'description' => '', + 'properties' => + array ( + '' => + array ( + 'type' => 'array', + 'description' => 'array of objects (JmsTest)', + 'items' => + array ( + '$ref' => 'Nelmio.ApiDocBundle.Tests.Fixtures.Model.JmsTest', + ), + ), + ), + 'required' => + array ( + 0 => '', + ), + ), + 'Nelmio.ApiDocBundle.Tests.Fixtures.Model.JmsNested' => + array ( + 'id' => 'Nelmio.ApiDocBundle.Tests.Fixtures.Model.JmsNested', + 'description' => 'object (JmsNested)', + 'properties' => + array ( + 'foo' => + array ( + 'type' => 'string', + 'description' => 'DateTime', + 'format' => 'date-time', + ), + 'bar' => + array ( + 'type' => 'string', + 'description' => 'string', + ), + 'baz' => + array ( + 'type' => 'array', + 'description' => 'Epic description. + +With multiple lines.', + 'items' => + array ( + 'type' => 'string', + ), + ), + 'circular' => + array ( + '$ref' => 'Nelmio.ApiDocBundle.Tests.Fixtures.Model.JmsNested', + ), + 'parent' => + array ( + '$ref' => 'Nelmio.ApiDocBundle.Tests.Fixtures.Model.JmsTest', + ), + 'since' => + array ( + 'type' => 'string', + 'description' => 'string', + ), + 'until' => + array ( + 'type' => 'string', + 'description' => 'string', + ), + 'since_and_until' => + array ( + 'type' => 'string', + 'description' => 'string', + ), + ), + 'required' => + array ( + ), + ), + ), 'produces' => array(), 'consumes' => From 928a23e2c8618acb6a4038f75467f458d6e04de5 Mon Sep 17 00:00:00 2001 From: Bez Hermoso Date: Thu, 7 Aug 2014 17:29:53 -0700 Subject: [PATCH 3/7] Updated regex pattern matching and added tests for parsing array<..> directives. --- Extractor/ApiDocExtractor.php | 2 +- Tests/Extractor/ApiDocExtractorTest.php | 39 +++++++++++++++++++++++++ Tests/Extractor/TestExtractor.php | 19 ++++++++++++ 3 files changed, 59 insertions(+), 1 deletion(-) create mode 100644 Tests/Extractor/TestExtractor.php diff --git a/Extractor/ApiDocExtractor.php b/Extractor/ApiDocExtractor.php index ed05a3c..19f3104 100644 --- a/Extractor/ApiDocExtractor.php +++ b/Extractor/ApiDocExtractor.php @@ -374,7 +374,7 @@ class ApiDocExtractor } $collectionData = array(); - preg_match_all("/array<(.*)>( as (.*))?/", $input['class'], $collectionData); + preg_match_all("/array<(.*)>(\\s+as\\s+(.*))?/", $input['class'], $collectionData); if (count($collectionData[0]) > 0) { $input['class'] = $collectionData[1][0]; diff --git a/Tests/Extractor/ApiDocExtractorTest.php b/Tests/Extractor/ApiDocExtractorTest.php index a0dbd29..a208b6e 100644 --- a/Tests/Extractor/ApiDocExtractorTest.php +++ b/Tests/Extractor/ApiDocExtractorTest.php @@ -256,4 +256,43 @@ class ApiDocExtractorTest extends WebTestCase ); $this->assertCount(1, $parsers); } + + public function testCollectionOutputNormalization() + { + $extractor = new TestExtractor(); + $normalized = $extractor->getNormalization('array'); + + $this->assertArrayHasKey('class', $normalized); + $this->assertArrayHasKey('collection', $normalized); + $this->assertArrayHasKey('collectionName', $normalized); + + $this->assertEquals('Vendor\\Namespace\\Test', $normalized['class']); + $this->assertEquals('', $normalized['collectionName']); + $this->assertTrue($normalized['collection']); + + } + + public function testNamedCollectionOutputNormalization() + { + $extractor = new TestExtractor(); + $normalized = $extractor->getNormalization('array as tests'); + + $this->assertArrayHasKey('class', $normalized); + $this->assertArrayHasKey('collection', $normalized); + $this->assertArrayHasKey('collectionName', $normalized); + + $this->assertEquals('Vendor\\Namespace\\Test', $normalized['class']); + $this->assertEquals('tests', $normalized['collectionName']); + $this->assertTrue($normalized['collection']); + } + + public function testFailedCollectionOutputNormalization() + { + $extractor = new TestExtractor(); + $normalized = $extractor->getNormalization('arrayassertArrayNotHasKey('collection', $normalized); + $this->assertArrayNotHasKey('collectionName', $normalized); + + } } diff --git a/Tests/Extractor/TestExtractor.php b/Tests/Extractor/TestExtractor.php new file mode 100644 index 0000000..da043b5 --- /dev/null +++ b/Tests/Extractor/TestExtractor.php @@ -0,0 +1,19 @@ +normalizeClassParameter($input); + } +} From 4b7dbcd478cca9e2c136872533f962b86371eb03 Mon Sep 17 00:00:00 2001 From: Bez Hermoso Date: Fri, 8 Aug 2014 08:51:21 -0700 Subject: [PATCH 4/7] Improved directive parsing, and separate test class for parsing directives. --- Extractor/ApiDocExtractor.php | 15 ++- Tests/Extractor/ApiDocExtractorTest.php | 39 ------- Tests/Extractor/CollectionDirectiveTest.php | 109 ++++++++++++++++++++ 3 files changed, 121 insertions(+), 42 deletions(-) create mode 100644 Tests/Extractor/CollectionDirectiveTest.php diff --git a/Extractor/ApiDocExtractor.php b/Extractor/ApiDocExtractor.php index 19f3104..725ddfc 100644 --- a/Extractor/ApiDocExtractor.php +++ b/Extractor/ApiDocExtractor.php @@ -374,12 +374,21 @@ class ApiDocExtractor } $collectionData = array(); - preg_match_all("/array<(.*)>(\\s+as\\s+(.*))?/", $input['class'], $collectionData); - if (count($collectionData[0]) > 0) { + /* + * Match array as alias; "as alias" optional. + */ + if (preg_match_all("/^array<([A-Za-z]+[A-Za-z0-9_]*(?:\\\\[A-Za-z]+[A-Za-z0-9_]*)*)>(?:\\s+as\\s+(.+))?$/", $input['class'], $collectionData)) { $input['class'] = $collectionData[1][0]; $input['collection'] = true; - $input['collectionName'] = $collectionData[3][0]; + $input['collectionName'] = $collectionData[2][0]; + } elseif (preg_match('/^array or array as collectionName', + $input['class'] + ) + ); } // normalize groups diff --git a/Tests/Extractor/ApiDocExtractorTest.php b/Tests/Extractor/ApiDocExtractorTest.php index a208b6e..a0dbd29 100644 --- a/Tests/Extractor/ApiDocExtractorTest.php +++ b/Tests/Extractor/ApiDocExtractorTest.php @@ -256,43 +256,4 @@ class ApiDocExtractorTest extends WebTestCase ); $this->assertCount(1, $parsers); } - - public function testCollectionOutputNormalization() - { - $extractor = new TestExtractor(); - $normalized = $extractor->getNormalization('array'); - - $this->assertArrayHasKey('class', $normalized); - $this->assertArrayHasKey('collection', $normalized); - $this->assertArrayHasKey('collectionName', $normalized); - - $this->assertEquals('Vendor\\Namespace\\Test', $normalized['class']); - $this->assertEquals('', $normalized['collectionName']); - $this->assertTrue($normalized['collection']); - - } - - public function testNamedCollectionOutputNormalization() - { - $extractor = new TestExtractor(); - $normalized = $extractor->getNormalization('array as tests'); - - $this->assertArrayHasKey('class', $normalized); - $this->assertArrayHasKey('collection', $normalized); - $this->assertArrayHasKey('collectionName', $normalized); - - $this->assertEquals('Vendor\\Namespace\\Test', $normalized['class']); - $this->assertEquals('tests', $normalized['collectionName']); - $this->assertTrue($normalized['collection']); - } - - public function testFailedCollectionOutputNormalization() - { - $extractor = new TestExtractor(); - $normalized = $extractor->getNormalization('arrayassertArrayNotHasKey('collection', $normalized); - $this->assertArrayNotHasKey('collectionName', $normalized); - - } } diff --git a/Tests/Extractor/CollectionDirectiveTest.php b/Tests/Extractor/CollectionDirectiveTest.php new file mode 100644 index 0000000..c432726 --- /dev/null +++ b/Tests/Extractor/CollectionDirectiveTest.php @@ -0,0 +1,109 @@ +testExtractor = new TestExtractor(); + } + + private function normalize($input) + { + return $this->testExtractor->getNormalization($input); + } + + /** + * @dataProvider dataNormalizationTests + */ + public function testNormalizations($input, callable $callable) + { + call_user_func($callable, $this->normalize($input), $this); + } + + public function dataNormalizationTests() + { + return array( + 'test_simple_notation' => array( + 'array', + function ($actual, \PHPUnit_Framework_TestCase $case) { + $case->assertArrayHasKey('collection', $actual); + $case->assertArrayHasKey('collectionName', $actual); + $case->assertArrayHasKey('class', $actual); + + $case->assertTrue($actual['collection']); + $case->assertEquals('', $actual['collectionName']); + $case->assertEquals('User', $actual['class']); + } + ), + 'test_simple_notation_with_namespaces' => array( + 'array', + function ($actual, \PHPUnit_Framework_TestCase $case) { + $case->assertArrayHasKey('collection', $actual); + $case->assertArrayHasKey('collectionName', $actual); + $case->assertArrayHasKey('class', $actual); + + $case->assertTrue($actual['collection']); + $case->assertEquals('', $actual['collectionName']); + $case->assertEquals('Vendor0_2\\Namespace1\\Namespace_2\\User', $actual['class']); + } + ), + 'test_simple_named_collections' => array( + 'array as groups', + function ($actual, \PHPUnit_Framework_TestCase $case) { + $case->assertArrayHasKey('collection', $actual); + $case->assertArrayHasKey('collectionName', $actual); + $case->assertArrayHasKey('class', $actual); + + $case->assertTrue($actual['collection']); + $case->assertEquals('groups', $actual['collectionName']); + $case->assertEquals('Group', $actual['class']); + } + ), + 'test_namespaced_named_collections' => array( + 'array as groups', + function ($actual, \PHPUnit_Framework_TestCase $case) { + $case->assertArrayHasKey('collection', $actual); + $case->assertArrayHasKey('collectionName', $actual); + $case->assertArrayHasKey('class', $actual); + + $case->assertTrue($actual['collection']); + $case->assertEquals('groups', $actual['collectionName']); + $case->assertEquals('Vendor\\Namespace0\\Namespace_2F3\\Group', $actual['class']); + } + ), + + ); + } + + /** + * @expectedException \InvalidArgumentException + * @dataProvider dataInvalidDirectives + * @param $input + */ + public function testInvalidDirectives($input) + { + $this->normalize($input); + } + + public function dataInvalidDirectives() + { + return array( + array('array<>'), + array('array'), + array('array<2Vendor\\>'), + array('array<_Vendor\\>'), + array('array'), + array('array'), + array('array as'), + array('array as '), + ); + } +} From c56aceaef5ab4ebec4f22a650aba32bd5273d441 Mon Sep 17 00:00:00 2001 From: Bez Hermoso Date: Fri, 8 Aug 2014 09:03:59 -0700 Subject: [PATCH 5/7] Updated regex pattern to base on http://fr2.php.net/manual/en/language.oop5.basic.php --- Extractor/ApiDocExtractor.php | 2 +- Tests/Extractor/CollectionDirectiveTest.php | 10 ++++------ 2 files changed, 5 insertions(+), 7 deletions(-) diff --git a/Extractor/ApiDocExtractor.php b/Extractor/ApiDocExtractor.php index 725ddfc..9241516 100644 --- a/Extractor/ApiDocExtractor.php +++ b/Extractor/ApiDocExtractor.php @@ -378,7 +378,7 @@ class ApiDocExtractor /* * Match array as alias; "as alias" optional. */ - if (preg_match_all("/^array<([A-Za-z]+[A-Za-z0-9_]*(?:\\\\[A-Za-z]+[A-Za-z0-9_]*)*)>(?:\\s+as\\s+(.+))?$/", $input['class'], $collectionData)) { + if (preg_match_all("/^array<([a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*(?:\\\\[a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*)*)>(?:\\s+as\\s+(.+))?$/", $input['class'], $collectionData)) { $input['class'] = $collectionData[1][0]; $input['collection'] = true; $input['collectionName'] = $collectionData[2][0]; diff --git a/Tests/Extractor/CollectionDirectiveTest.php b/Tests/Extractor/CollectionDirectiveTest.php index c432726..1666dc8 100644 --- a/Tests/Extractor/CollectionDirectiveTest.php +++ b/Tests/Extractor/CollectionDirectiveTest.php @@ -44,7 +44,7 @@ class CollectionDirectiveTest extends \PHPUnit_Framework_TestCase } ), 'test_simple_notation_with_namespaces' => array( - 'array', + 'array', function ($actual, \PHPUnit_Framework_TestCase $case) { $case->assertArrayHasKey('collection', $actual); $case->assertArrayHasKey('collectionName', $actual); @@ -52,7 +52,7 @@ class CollectionDirectiveTest extends \PHPUnit_Framework_TestCase $case->assertTrue($actual['collection']); $case->assertEquals('', $actual['collectionName']); - $case->assertEquals('Vendor0_2\\Namespace1\\Namespace_2\\User', $actual['class']); + $case->assertEquals('Vendor0_2\\_Namespace1\\Namespace_2\\User', $actual['class']); } ), 'test_simple_named_collections' => array( @@ -68,7 +68,7 @@ class CollectionDirectiveTest extends \PHPUnit_Framework_TestCase } ), 'test_namespaced_named_collections' => array( - 'array as groups', + 'array<_Vendor\\Namespace0\\Namespace_2F3\\Group> as groups', function ($actual, \PHPUnit_Framework_TestCase $case) { $case->assertArrayHasKey('collection', $actual); $case->assertArrayHasKey('collectionName', $actual); @@ -76,7 +76,7 @@ class CollectionDirectiveTest extends \PHPUnit_Framework_TestCase $case->assertTrue($actual['collection']); $case->assertEquals('groups', $actual['collectionName']); - $case->assertEquals('Vendor\\Namespace0\\Namespace_2F3\\Group', $actual['class']); + $case->assertEquals('_Vendor\\Namespace0\\Namespace_2F3\\Group', $actual['class']); } ), @@ -99,8 +99,6 @@ class CollectionDirectiveTest extends \PHPUnit_Framework_TestCase array('array<>'), array('array'), array('array<2Vendor\\>'), - array('array<_Vendor\\>'), - array('array'), array('array'), array('array as'), array('array as '), From 5fa69a05045fd0de6fe90f8eead7d107fb8dbbec Mon Sep 17 00:00:00 2001 From: Bez Hermoso Date: Tue, 12 Aug 2014 18:41:23 -0700 Subject: [PATCH 6/7] Tests for aliased collections; Swagger formatting for wrapped collections. --- Formatter/SwaggerFormatter.php | 46 +- Swagger/ModelRegistry.php | 17 +- .../Controller/ResourceController.php | 1 + Tests/Formatter/MarkdownFormatterTest.php | 14 + Tests/Formatter/SimpleFormatterTest.php | 35 + Tests/Formatter/SwaggerFormatterTest.php | 790 ++++++++++-------- 6 files changed, 549 insertions(+), 354 deletions(-) diff --git a/Formatter/SwaggerFormatter.php b/Formatter/SwaggerFormatter.php index a8bfe7c..d56316d 100644 --- a/Formatter/SwaggerFormatter.php +++ b/Formatter/SwaggerFormatter.php @@ -11,7 +11,6 @@ namespace Nelmio\ApiDocBundle\Formatter; - use Nelmio\ApiDocBundle\Annotation\ApiDoc; use Nelmio\ApiDocBundle\DataTypes; use Nelmio\ApiDocBundle\Swagger\ModelRegistry; @@ -276,11 +275,44 @@ class SwaggerFormatter implements FormatterInterface $message = sprintf('See standard HTTP status code reason for %s', $statusCode); } - $responseModel = array( - 'code' => $statusCode, - 'message' => $message, - 'responseModel' => $this->registerModel($prop['type']['class'], $prop['model'], ''), - ); + if (isset($prop['type']['collection']) && $prop['type']['collection'] === true) { + + /* + * Without alias: Fully\Qualified\Class\Name[] + * With alias: Fully\Qualified\Class\Name[alias] + */ + $alias = $prop['type']['collectionName']; + + $newName = sprintf('%s[%s]', $prop['type']['class'], $alias); + $collId = + $this->registerModel( + $newName, + array( + $alias => array( + 'dataType' => null, + 'subType' => $prop['type']['class'], + 'actualType' => DataTypes::COLLECTION, + 'required' => true, + 'readonly' => true, + 'description' => null, + 'default' => null, + 'children' => $prop['model'][$alias]['children'], + ) + ), + '' + ); + $responseModel = array( + 'code' => $statusCode, + 'message' => $message, + 'responseModel' => $collId + ); + } else { + $responseModel = array( + 'code' => $statusCode, + 'message' => $message, + 'responseModel' => $this->registerModel($prop['type']['class'], $prop['model'], ''), + ); + } $responseMessages[$statusCode] = $responseModel; } @@ -423,7 +455,7 @@ class SwaggerFormatter implements FormatterInterface $prop['description'] ?: $prop['dataType'] ); $items = array( - '$ref' => $ref, + '$ref' => $ref ); } elseif (isset($this->typeMap[$prop['subType']])) { $items = array('type' => $this->typeMap[$prop['subType']]); diff --git a/Swagger/ModelRegistry.php b/Swagger/ModelRegistry.php index 327a274..3f4ea23 100644 --- a/Swagger/ModelRegistry.php +++ b/Swagger/ModelRegistry.php @@ -130,14 +130,13 @@ class ModelRegistry case DataTypes::COLLECTION: $type = 'array'; - if ($prop['subType'] === DataTypes::MODEL) { - - } else { - - if ($prop['subType'] === null - || isset($this->typeMap[$prop['subType']])) { + if ($prop['subType'] === null) { + $items = array( + 'type' => 'string', + ); + } elseif (isset($this->typeMap[$prop['subType']])) { $items = array( - 'type' => 'string', + 'type' => $this->typeMap[$prop['subType']] ); } elseif (!isset($this->typeMap[$prop['subType']])) { $items = array( @@ -149,7 +148,6 @@ class ModelRegistry ) ); } - } /* @TODO: Handle recursion if subtype is a model. */ break; @@ -213,8 +211,9 @@ class ModelRegistry { /* * Converts \Fully\Qualified\Class\Name to Fully.Qualified.Class.Name + * "[...]" in aliased and non-aliased collections preserved. */ - $id = preg_replace('#(\\\|[^A-Za-z0-9])#', '.', $className); + $id = preg_replace('#(\\\|[^A-Za-z0-9\[\]])#', '.', $className); //Replace duplicate dots. $id = preg_replace('/\.+/', '.', $id); //Replace trailing dots. diff --git a/Tests/Fixtures/Controller/ResourceController.php b/Tests/Fixtures/Controller/ResourceController.php index 74aad93..ec75544 100644 --- a/Tests/Fixtures/Controller/ResourceController.php +++ b/Tests/Fixtures/Controller/ResourceController.php @@ -19,6 +19,7 @@ class ResourceController * resource=true, * resourceDescription="Operations on resource.", * description="List resources.", + * output="array as tests", * statusCodes={200 = "Returned on success.", 404 = "Returned if resource cannot be found."} * ) */ diff --git a/Tests/Formatter/MarkdownFormatterTest.php b/Tests/Formatter/MarkdownFormatterTest.php index bbf59f1..7b5caa1 100644 --- a/Tests/Formatter/MarkdownFormatterTest.php +++ b/Tests/Formatter/MarkdownFormatterTest.php @@ -156,6 +156,20 @@ _List resources._ - Requirement: json|xml|html +#### Response #### + +tests[]: + + * type: array of objects (Test) + +tests[][a]: + + * type: string + +tests[][b]: + + * type: DateTime + ### `POST` /api/resources.{_format} ### diff --git a/Tests/Formatter/SimpleFormatterTest.php b/Tests/Formatter/SimpleFormatterTest.php index f9bcf3d..380b813 100644 --- a/Tests/Formatter/SimpleFormatterTest.php +++ b/Tests/Formatter/SimpleFormatterTest.php @@ -1655,6 +1655,41 @@ With multiple lines.', 'authenticationRoles' => array(), 'deprecated' => false, + 'response' => + array ( + 'tests' => + array ( + 'dataType' => 'array of objects (Test)', + 'subType' => 'Nelmio\\ApiDocBundle\\Tests\\Fixtures\\Model\\Test', + 'actualType' => 'collection', + 'readonly' => true, + 'required' => true, + 'default' => true, + 'description' => '', + 'children' => + array ( + 'a' => + array ( + 'default' => 'nelmio', + 'actualType' => 'string', + 'subType' => NULL, + 'format' => '{length: min: foo}, {not blank}', + 'required' => true, + 'dataType' => 'string', + 'readonly' => NULL, + ), + 'b' => + array ( + 'default' => NULL, + 'actualType' => 'datetime', + 'subType' => NULL, + 'dataType' => 'DateTime', + 'readonly' => NULL, + 'required' => NULL, + ), + ), + ), + ), ), array( 'method' => 'POST', diff --git a/Tests/Formatter/SwaggerFormatterTest.php b/Tests/Formatter/SwaggerFormatterTest.php index 10d83c7..47d4e56 100644 --- a/Tests/Formatter/SwaggerFormatterTest.php +++ b/Tests/Formatter/SwaggerFormatterTest.php @@ -121,456 +121,546 @@ class SwaggerFormatterTest extends WebTestCase return array( array( '/resources', - array( + array ( 'swaggerVersion' => '1.2', 'apiVersion' => '3.14', 'basePath' => '/api', 'resourcePath' => '/resources', 'apis' => - array( - - array( - 'path' => '/resources.{_format}', - 'operations' => - array( - array( - 'method' => 'GET', - 'summary' => 'List resources.', - 'nickname' => 'get_resources', - 'parameters' => - array( - - array( - 'paramType' => 'path', - 'name' => '_format', - 'type' => 'string', - 'required' => true, - 'enum' => - array( - 'json', - 'xml', - 'html', - ), - ), + array ( + 0 => + array ( + 'path' => '/resources.{_format}', + 'operations' => + array ( + 0 => + array ( + 'method' => 'GET', + 'summary' => 'List resources.', + 'nickname' => 'get_resources', + 'parameters' => + array ( + 0 => + array ( + 'paramType' => 'path', + 'name' => '_format', + 'type' => 'string', + 'required' => true, + 'enum' => + array ( + 0 => 'json', + 1 => 'xml', + 2 => 'html', + ), + ), + ), + 'responseMessages' => + array ( + 0 => + array ( + 'code' => 200, + 'message' => 'Returned on success.', + 'responseModel' => 'Nelmio.ApiDocBundle.Tests.Fixtures.Model.Test[tests]', + ), + 1 => + array ( + 'code' => 404, + 'message' => 'Returned if resource cannot be found.', + ), + ), + 'type' => 'Nelmio.ApiDocBundle.Tests.Fixtures.Model.Test[tests]', ), - 'responseMessages' => - array( - - array( - 'code' => 200, - 'message' => 'Returned on success.', - ), - - array( - 'code' => 404, - 'message' => 'Returned if resource cannot be found.', - ), + 1 => + array ( + 'method' => 'POST', + 'summary' => 'Create a new resource.', + 'nickname' => 'post_resources', + 'parameters' => + array ( + 0 => + array ( + 'paramType' => 'path', + 'name' => '_format', + 'type' => 'string', + 'required' => true, + 'enum' => + array ( + 0 => 'json', + 1 => 'xml', + 2 => 'html', + ), + ), + 1 => + array ( + 'paramType' => 'form', + 'name' => 'a', + 'type' => 'string', + ), + 2 => + array ( + 'paramType' => 'form', + 'name' => 'b', + 'type' => 'number', + 'format' => 'float', + ), + 3 => + array ( + 'paramType' => 'form', + 'name' => 'c', + 'type' => 'string', + 'enum' => + array ( + 0 => 'x', + 1 => 'y', + 2 => 'z', + ), + ), + 4 => + array ( + 'paramType' => 'form', + 'name' => 'd', + 'type' => 'string', + 'format' => 'date-time', + ), + 5 => + array ( + 'paramType' => 'form', + 'name' => 'e', + 'type' => 'string', + 'format' => 'date', + ), + 6 => + array ( + 'paramType' => 'form', + 'name' => 'g', + 'type' => 'string', + ), + ), + 'responseMessages' => + array ( + 0 => + array ( + 'code' => 200, + 'message' => 'See standard HTTP status code reason for 200', + 'responseModel' => 'Nelmio.ApiDocBundle.Tests.Fixtures.Model.JmsNested', + ), + ), + 'type' => 'Nelmio.ApiDocBundle.Tests.Fixtures.Model.JmsNested', ), ), - - array( - 'method' => 'POST', - 'summary' => 'Create a new resource.', - 'nickname' => 'post_resources', - 'parameters' => - array( - - array( - 'paramType' => 'path', - 'name' => '_format', - 'type' => 'string', - 'required' => true, - 'enum' => - array( - 'json', - 'xml', - 'html', - ), - ), - - array( - 'paramType' => 'form', - 'name' => 'a', - 'type' => 'string', - ), - - array( - 'paramType' => 'form', - 'name' => 'b', - 'type' => 'number', - 'format' => 'float', - ), - - array( - 'paramType' => 'form', - 'name' => 'c', - 'type' => 'string', - 'enum' => - array( - 'x', - 'y', - 'z', - ), - ), - - array( - 'paramType' => 'form', - 'name' => 'd', - 'type' => 'string', - 'format' => 'date-time', - ), - - array( - 'paramType' => 'form', - 'name' => 'e', - 'type' => 'string', - 'format' => 'date', - ), - - array( - 'paramType' => 'form', - 'name' => 'g', - 'type' => 'string', - ), + ), + 1 => + array ( + 'path' => '/resources/{id}.{_format}', + 'operations' => + array ( + 0 => + array ( + 'method' => 'GET', + 'summary' => 'Retrieve a resource by ID.', + 'nickname' => 'get_resources', + 'parameters' => + array ( + 0 => + array ( + 'paramType' => 'path', + 'name' => 'id', + 'type' => 'string', + 'required' => true, + ), + 1 => + array ( + 'paramType' => 'path', + 'name' => '_format', + 'type' => 'string', + 'required' => true, + 'enum' => + array ( + 0 => 'json', + 1 => 'xml', + 2 => 'html', + ), + ), + ), + 'responseMessages' => + array ( + ), ), - 'responseMessages' => - array( - array( - 'code' => 200, - 'message' => 'See standard HTTP status code reason for 200', - 'responseModel' => 'Nelmio.ApiDocBundle.Tests.Fixtures.Model.JmsNested', - ), + 1 => + array ( + 'method' => 'DELETE', + 'summary' => 'Delete a resource by ID.', + 'nickname' => 'delete_resources', + 'parameters' => + array ( + 0 => + array ( + 'paramType' => 'path', + 'name' => 'id', + 'type' => 'string', + 'required' => true, + ), + 1 => + array ( + 'paramType' => 'path', + 'name' => '_format', + 'type' => 'string', + 'required' => true, + 'enum' => + array ( + 0 => 'json', + 1 => 'xml', + 2 => 'html', + ), + ), + ), + 'responseMessages' => + array ( + ), ), - 'type' => 'Nelmio.ApiDocBundle.Tests.Fixtures.Model.JmsNested', ), - ), - ), - - array( - 'path' => '/resources/{id}.{_format}', - 'operations' => - array( - - array( - 'method' => 'GET', - 'summary' => 'Retrieve a resource by ID.', - 'nickname' => 'get_resources', - 'parameters' => - array( - - array( - 'paramType' => 'path', - 'name' => 'id', - 'type' => 'string', - 'required' => true, - ), - - array( - 'paramType' => 'path', - 'name' => '_format', - 'type' => 'string', - 'required' => true, - 'enum' => - array( - 'json', - 'xml', - 'html', - ), - ), - ), - 'responseMessages' => - array(), - ), - - array( - 'method' => 'DELETE', - 'summary' => 'Delete a resource by ID.', - 'nickname' => 'delete_resources', - 'parameters' => - array( - - array( - 'paramType' => 'path', - 'name' => 'id', - 'type' => 'string', - 'required' => true, - ), - - array( - 'paramType' => 'path', - 'name' => '_format', - 'type' => 'string', - 'required' => true, - 'enum' => - array( - 'json', - 'xml', - 'html', - ), - ), - ), - 'responseMessages' => - array(), - ), - ), - ), + ), ), 'models' => - array( + array ( + 'Nelmio.ApiDocBundle.Tests.Fixtures.Model.Test' => + array ( + 'id' => 'Nelmio.ApiDocBundle.Tests.Fixtures.Model.Test', + 'description' => NULL, + 'properties' => + array ( + 'a' => + array ( + 'type' => 'string', + 'description' => 'string', + ), + 'b' => + array ( + 'type' => 'string', + 'description' => 'DateTime', + 'format' => 'date-time', + ), + ), + 'required' => + array ( + 0 => 'a', + ), + ), + 'Nelmio.ApiDocBundle.Tests.Fixtures.Model.Test[tests]' => + array ( + 'id' => 'Nelmio.ApiDocBundle.Tests.Fixtures.Model.Test[tests]', + 'description' => '', + 'properties' => + array ( + 'tests' => + array ( + 'type' => 'array', + 'description' => NULL, + 'items' => + array ( + '$ref' => 'Nelmio.ApiDocBundle.Tests.Fixtures.Model.Test', + ), + ), + ), + 'required' => + array ( + 0 => 'tests', + ), + ), 'Nelmio.ApiDocBundle.Tests.Fixtures.Model.JmsTest' => - array( + array ( 'id' => 'Nelmio.ApiDocBundle.Tests.Fixtures.Model.JmsTest', 'description' => 'object (JmsTest)', 'properties' => - array( + array ( 'foo' => - array( + array ( 'type' => 'string', 'description' => 'string', ), 'bar' => - array( + array ( 'type' => 'string', 'description' => 'DateTime', 'format' => 'date-time', ), 'number' => - array( + array ( 'type' => 'number', 'description' => 'double', 'format' => 'float', ), 'arr' => - array( + array ( 'type' => 'array', 'description' => 'array', - 'items' => array( - 'type' => 'string', - ) + 'items' => + array ( + 'type' => 'string', + ), ), 'nested' => - array( + array ( '$ref' => 'Nelmio.ApiDocBundle.Tests.Fixtures.Model.JmsNested', ), 'nested_array' => - array( + array ( 'type' => 'array', 'description' => 'array of objects (JmsNested)', - 'items' => array( - '$ref' => 'Nelmio.ApiDocBundle.Tests.Fixtures.Model.JmsNested', - ) + 'items' => + array ( + '$ref' => 'Nelmio.ApiDocBundle.Tests.Fixtures.Model.JmsNested', + ), ), ), 'required' => - array(), + array ( + ), ), 'Nelmio.ApiDocBundle.Tests.Fixtures.Model.JmsNested' => - array( + array ( 'id' => 'Nelmio.ApiDocBundle.Tests.Fixtures.Model.JmsNested', 'description' => '', 'properties' => - array( + array ( 'foo' => - array( + array ( 'type' => 'string', 'description' => 'DateTime', 'format' => 'date-time', ), 'bar' => - array( + array ( 'type' => 'string', 'description' => 'string', ), 'baz' => - array( + array ( 'type' => 'array', 'description' => 'Epic description. With multiple lines.', - 'items' => array( - 'type' => 'string', - ) + 'items' => + array ( + 'type' => 'integer', + ), ), 'circular' => - array( + array ( '$ref' => 'Nelmio.ApiDocBundle.Tests.Fixtures.Model.JmsNested', ), 'parent' => - array( + array ( '$ref' => 'Nelmio.ApiDocBundle.Tests.Fixtures.Model.JmsTest', ), 'since' => - array( + array ( 'type' => 'string', 'description' => 'string', ), 'until' => - array( + array ( 'type' => 'string', 'description' => 'string', ), 'since_and_until' => - array( + array ( 'type' => 'string', 'description' => 'string', ), ), 'required' => - array(), + array ( + ), ), ), 'produces' => - array(), + array ( + ), 'consumes' => - array(), + array ( + ), 'authorizations' => - array( - 'apiKey' => array( - 'type' => 'apiKey', - 'passAs' => 'header', - 'keyname' => 'access_token', - ) + array ( + 'apiKey' => + array ( + 'type' => 'apiKey', + 'passAs' => 'header', + 'keyname' => 'access_token', + ), ), ), ), array( '/other-resources', - array( + array ( 'swaggerVersion' => '1.2', 'apiVersion' => '3.14', 'basePath' => '/api', 'resourcePath' => '/other-resources', 'apis' => - array( - - array( - 'path' => '/other-resources.{_format}', - 'operations' => - array( - - array( - 'method' => 'GET', - 'summary' => 'List another resource.', - 'nickname' => 'get_other-resources', - 'type' => 'Nelmio.ApiDocBundle.Tests.Fixtures.Model.JmsTest', - 'parameters' => - array( - - array( - 'paramType' => 'path', - 'name' => '_format', - 'type' => 'string', - 'required' => true, - 'enum' => - array( - 'json', - 'xml', - 'html', - ), - ), - ), - 'responseMessages' => - array( - array( - 'code' => 200, - 'message' => 'See standard HTTP status code reason for 200', - 'responseModel' => 'Nelmio.ApiDocBundle.Tests.Fixtures.Model.JmsTest', - ), + array ( + 0 => + array ( + 'path' => '/other-resources.{_format}', + 'operations' => + array ( + 0 => + array ( + 'method' => 'GET', + 'summary' => 'List another resource.', + 'nickname' => 'get_other-resources', + 'parameters' => + array ( + 0 => + array ( + 'paramType' => 'path', + 'name' => '_format', + 'type' => 'string', + 'required' => true, + 'enum' => + array ( + 0 => 'json', + 1 => 'xml', + 2 => 'html', + ), + ), + ), + 'responseMessages' => + array ( + 0 => + array ( + 'code' => 200, + 'message' => 'See standard HTTP status code reason for 200', + 'responseModel' => 'Nelmio.ApiDocBundle.Tests.Fixtures.Model.JmsTest[]', + ), + ), + 'type' => 'Nelmio.ApiDocBundle.Tests.Fixtures.Model.JmsTest[]', ), ), - ), - ), - - array( - 'path' => '/other-resources/{id}.{_format}', - 'operations' => - array( - - array( - 'method' => 'PUT', - 'summary' => 'Update a resource bu ID.', - 'nickname' => 'put_other-resources', - 'parameters' => - array( - - array( - 'paramType' => 'path', - 'name' => 'id', - 'type' => 'string', - 'required' => true, - ), - - array( - 'paramType' => 'path', - 'name' => '_format', - 'type' => 'string', - 'required' => true, - 'enum' => - array( - 'json', - 'xml', - 'html', - ), - ), + ), + 1 => + array ( + 'path' => '/other-resources/{id}.{_format}', + 'operations' => + array ( + 0 => + array ( + 'method' => 'PUT', + 'summary' => 'Update a resource bu ID.', + 'nickname' => 'put_other-resources', + 'parameters' => + array ( + 0 => + array ( + 'paramType' => 'path', + 'name' => 'id', + 'type' => 'string', + 'required' => true, + ), + 1 => + array ( + 'paramType' => 'path', + 'name' => '_format', + 'type' => 'string', + 'required' => true, + 'enum' => + array ( + 0 => 'json', + 1 => 'xml', + 2 => 'html', + ), + ), + ), + 'responseMessages' => + array ( + ), ), - 'responseMessages' => - array(), - ), - - array( - 'method' => 'PATCH', - 'summary' => 'Update a resource bu ID.', - 'nickname' => 'patch_other-resources', - 'parameters' => - array( - - array( - 'paramType' => 'path', - 'name' => 'id', - 'type' => 'string', - 'required' => true, - ), - - array( - 'paramType' => 'path', - 'name' => '_format', - 'type' => 'string', - 'required' => true, - 'enum' => - array( - 'json', - 'xml', - 'html', - ), - ), + 1 => + array ( + 'method' => 'PATCH', + 'summary' => 'Update a resource bu ID.', + 'nickname' => 'patch_other-resources', + 'parameters' => + array ( + 0 => + array ( + 'paramType' => 'path', + 'name' => 'id', + 'type' => 'string', + 'required' => true, + ), + 1 => + array ( + 'paramType' => 'path', + 'name' => '_format', + 'type' => 'string', + 'required' => true, + 'enum' => + array ( + 0 => 'json', + 1 => 'xml', + 2 => 'html', + ), + ), + ), + 'responseMessages' => + array ( + ), ), - 'responseMessages' => - array(), ), - ), - ), + ), ), 'models' => array ( 'Nelmio.ApiDocBundle.Tests.Fixtures.Model.JmsTest' => array ( 'id' => 'Nelmio.ApiDocBundle.Tests.Fixtures.Model.JmsTest', - 'description' => '', + 'description' => NULL, 'properties' => array ( - '' => + 'foo' => + array ( + 'type' => 'string', + 'description' => 'string', + ), + 'bar' => + array ( + 'type' => 'string', + 'description' => 'DateTime', + 'format' => 'date-time', + ), + 'number' => + array ( + 'type' => 'number', + 'description' => 'double', + 'format' => 'float', + ), + 'arr' => array ( 'type' => 'array', - 'description' => 'array of objects (JmsTest)', + 'description' => 'array', 'items' => array ( - '$ref' => 'Nelmio.ApiDocBundle.Tests.Fixtures.Model.JmsTest', + 'type' => 'string', + ), + ), + 'nested' => + array ( + '$ref' => 'Nelmio.ApiDocBundle.Tests.Fixtures.Model.JmsNested', + ), + 'nested_array' => + array ( + 'type' => 'array', + 'description' => 'array of objects (JmsNested)', + 'items' => + array ( + '$ref' => 'Nelmio.ApiDocBundle.Tests.Fixtures.Model.JmsNested', ), ), ), 'required' => array ( - 0 => '', ), ), 'Nelmio.ApiDocBundle.Tests.Fixtures.Model.JmsNested' => @@ -598,7 +688,7 @@ With multiple lines.', With multiple lines.', 'items' => array ( - 'type' => 'string', + 'type' => 'integer', ), ), 'circular' => @@ -629,18 +719,42 @@ With multiple lines.', array ( ), ), + 'Nelmio.ApiDocBundle.Tests.Fixtures.Model.JmsTest[]' => + array ( + 'id' => 'Nelmio.ApiDocBundle.Tests.Fixtures.Model.JmsTest[]', + 'description' => '', + 'properties' => + array ( + '' => + array ( + 'type' => 'array', + 'description' => NULL, + 'items' => + array ( + '$ref' => 'Nelmio.ApiDocBundle.Tests.Fixtures.Model.JmsTest', + ), + ), + ), + 'required' => + array ( + 0 => '', + ), + ), ), 'produces' => - array(), + array ( + ), 'consumes' => - array(), + array ( + ), 'authorizations' => - array( - 'apiKey' => array( - 'type' => 'apiKey', - 'passAs' => 'header', - 'keyname' => 'access_token', - ) + array ( + 'apiKey' => + array ( + 'type' => 'apiKey', + 'passAs' => 'header', + 'keyname' => 'access_token', + ), ), ), ), From 0d17c10b70c77626ef0a800b99b4c82c4ea0224b Mon Sep 17 00:00:00 2001 From: Bez Hermoso Date: Thu, 4 Sep 2014 11:19:02 -0700 Subject: [PATCH 7/7] Collection handling fix. --- Formatter/SwaggerFormatter.php | 21 +++++++++++---------- 1 file changed, 11 insertions(+), 10 deletions(-) diff --git a/Formatter/SwaggerFormatter.php b/Formatter/SwaggerFormatter.php index d56316d..8599959 100644 --- a/Formatter/SwaggerFormatter.php +++ b/Formatter/SwaggerFormatter.php @@ -448,19 +448,20 @@ class SwaggerFormatter implements FormatterInterface case DataTypes::COLLECTION: $type = 'array'; - if ($prop['subType'] === DataTypes::MODEL) { - $ref = $this->registerModel( - $prop['subType'], - isset($prop['children']) ? $prop['children'] : null, - $prop['description'] ?: $prop['dataType'] - ); - $items = array( - '$ref' => $ref - ); + if ($prop['subType'] === null) { + $items = array('type' => 'string'); } elseif (isset($this->typeMap[$prop['subType']])) { $items = array('type' => $this->typeMap[$prop['subType']]); } else { - $items = array('type' => 'string'); + $ref = + $this->registerModel( + $prop['subType'], + isset($prop['children']) ? $prop['children'] : null, + $prop['description'] ?: $prop['dataType'] + ); + $items = array( + '$ref' => $ref, + ); } break; }