Tests for aliased collections; Swagger formatting for wrapped collections.

This commit is contained in:
Bez Hermoso 2014-08-12 18:41:23 -07:00
parent c56aceaef5
commit 5fa69a0504
6 changed files with 549 additions and 354 deletions

View File

@ -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);
}
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']]);

View File

@ -130,15 +130,14 @@ 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' => $this->typeMap[$prop['subType']]
);
} elseif (!isset($this->typeMap[$prop['subType']])) {
$items = array(
'$ref' =>
@ -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.

View File

@ -19,6 +19,7 @@ class ResourceController
* resource=true,
* resourceDescription="Operations on resource.",
* description="List resources.",
* output="array<Nelmio\ApiDocBundle\Tests\Fixtures\Model\Test> as tests",
* statusCodes={200 = "Returned on success.", 404 = "Returned if resource cannot be found."}
* )
*/

View File

@ -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} ###

View File

@ -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',

View File

@ -121,121 +121,125 @@ class SwaggerFormatterTest extends WebTestCase
return array(
array(
'/resources',
array(
array (
'swaggerVersion' => '1.2',
'apiVersion' => '3.14',
'basePath' => '/api',
'resourcePath' => '/resources',
'apis' =>
array(
array(
array (
0 =>
array (
'path' => '/resources.{_format}',
'operations' =>
array(
array(
array (
0 =>
array (
'method' => 'GET',
'summary' => 'List resources.',
'nickname' => 'get_resources',
'parameters' =>
array(
array(
array (
0 =>
array (
'paramType' => 'path',
'name' => '_format',
'type' => 'string',
'required' => true,
'enum' =>
array(
'json',
'xml',
'html',
array (
0 => 'json',
1 => 'xml',
2 => 'html',
),
),
),
'responseMessages' =>
array(
array(
array (
0 =>
array (
'code' => 200,
'message' => 'Returned on success.',
'responseModel' => 'Nelmio.ApiDocBundle.Tests.Fixtures.Model.Test[tests]',
),
array(
1 =>
array (
'code' => 404,
'message' => 'Returned if resource cannot be found.',
),
),
'type' => 'Nelmio.ApiDocBundle.Tests.Fixtures.Model.Test[tests]',
),
array(
1 =>
array (
'method' => 'POST',
'summary' => 'Create a new resource.',
'nickname' => 'post_resources',
'parameters' =>
array(
array(
array (
0 =>
array (
'paramType' => 'path',
'name' => '_format',
'type' => 'string',
'required' => true,
'enum' =>
array(
'json',
'xml',
'html',
array (
0 => 'json',
1 => 'xml',
2 => 'html',
),
),
array(
1 =>
array (
'paramType' => 'form',
'name' => 'a',
'type' => 'string',
),
array(
2 =>
array (
'paramType' => 'form',
'name' => 'b',
'type' => 'number',
'format' => 'float',
),
array(
3 =>
array (
'paramType' => 'form',
'name' => 'c',
'type' => 'string',
'enum' =>
array(
'x',
'y',
'z',
array (
0 => 'x',
1 => 'y',
2 => 'z',
),
),
array(
4 =>
array (
'paramType' => 'form',
'name' => 'd',
'type' => 'string',
'format' => 'date-time',
),
array(
5 =>
array (
'paramType' => 'form',
'name' => 'e',
'type' => 'string',
'format' => 'date',
),
array(
6 =>
array (
'paramType' => 'form',
'name' => 'g',
'type' => 'string',
),
),
'responseMessages' =>
array(
array(
array (
0 =>
array (
'code' => 200,
'message' => 'See standard HTTP status code reason for 200',
'responseModel' => 'Nelmio.ApiDocBundle.Tests.Fixtures.Model.JmsNested',
@ -245,307 +249,364 @@ class SwaggerFormatterTest extends WebTestCase
),
),
),
array(
1 =>
array (
'path' => '/resources/{id}.{_format}',
'operations' =>
array(
array(
array (
0 =>
array (
'method' => 'GET',
'summary' => 'Retrieve a resource by ID.',
'nickname' => 'get_resources',
'parameters' =>
array(
array(
array (
0 =>
array (
'paramType' => 'path',
'name' => 'id',
'type' => 'string',
'required' => true,
),
array(
1 =>
array (
'paramType' => 'path',
'name' => '_format',
'type' => 'string',
'required' => true,
'enum' =>
array(
'json',
'xml',
'html',
array (
0 => 'json',
1 => 'xml',
2 => 'html',
),
),
),
'responseMessages' =>
array(),
array (
),
array(
),
1 =>
array (
'method' => 'DELETE',
'summary' => 'Delete a resource by ID.',
'nickname' => 'delete_resources',
'parameters' =>
array(
array(
array (
0 =>
array (
'paramType' => 'path',
'name' => 'id',
'type' => 'string',
'required' => true,
),
array(
1 =>
array (
'paramType' => 'path',
'name' => '_format',
'type' => 'string',
'required' => true,
'enum' =>
array(
'json',
'xml',
'html',
array (
0 => 'json',
1 => 'xml',
2 => 'html',
),
),
),
'responseMessages' =>
array(),
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(
'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(
'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(
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(
array (
0 =>
array (
'path' => '/other-resources.{_format}',
'operations' =>
array(
array(
array (
0 =>
array (
'method' => 'GET',
'summary' => 'List another resource.',
'nickname' => 'get_other-resources',
'type' => 'Nelmio.ApiDocBundle.Tests.Fixtures.Model.JmsTest',
'parameters' =>
array(
array(
array (
0 =>
array (
'paramType' => 'path',
'name' => '_format',
'type' => 'string',
'required' => true,
'enum' =>
array(
'json',
'xml',
'html',
array (
0 => 'json',
1 => 'xml',
2 => 'html',
),
),
),
'responseMessages' =>
array(
array(
array (
0 =>
array (
'code' => 200,
'message' => 'See standard HTTP status code reason for 200',
'responseModel' => 'Nelmio.ApiDocBundle.Tests.Fixtures.Model.JmsTest',
'responseModel' => 'Nelmio.ApiDocBundle.Tests.Fixtures.Model.JmsTest[]',
),
),
'type' => 'Nelmio.ApiDocBundle.Tests.Fixtures.Model.JmsTest[]',
),
),
),
),
),
array(
1 =>
array (
'path' => '/other-resources/{id}.{_format}',
'operations' =>
array(
array(
array (
0 =>
array (
'method' => 'PUT',
'summary' => 'Update a resource bu ID.',
'nickname' => 'put_other-resources',
'parameters' =>
array(
array(
array (
0 =>
array (
'paramType' => 'path',
'name' => 'id',
'type' => 'string',
'required' => true,
),
array(
1 =>
array (
'paramType' => 'path',
'name' => '_format',
'type' => 'string',
'required' => true,
'enum' =>
array(
'json',
'xml',
'html',
array (
0 => 'json',
1 => 'xml',
2 => 'html',
),
),
),
'responseMessages' =>
array(),
array (
),
array(
),
1 =>
array (
'method' => 'PATCH',
'summary' => 'Update a resource bu ID.',
'nickname' => 'patch_other-resources',
'parameters' =>
array(
array(
array (
0 =>
array (
'paramType' => 'path',
'name' => 'id',
'type' => 'string',
'required' => true,
),
array(
1 =>
array (
'paramType' => 'path',
'name' => '_format',
'type' => 'string',
'required' => true,
'enum' =>
array(
'json',
'xml',
'html',
array (
0 => 'json',
1 => 'xml',
2 => 'html',
),
),
),
'responseMessages' =>
array(),
array (
),
),
),
),
@ -555,22 +616,51 @@ With multiple lines.',
'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(
array (
'apiKey' =>
array (
'type' => 'apiKey',
'passAs' => 'header',
'keyname' => 'access_token',
)
),
),
),
),