Unified data types [actualType and subType]

This is the result of https://github.com/nelmio/NelmioApiDocBundle/issues/410.

This PR aims to provide a uniform way of declaring data-types of parameters for
parsers and handlers to follow. In turn, this would allow formatters to
determine data-types in a cleaner and less volatile manner. (See use-case that
can be improved with this PR:
https://github.com/nelmio/NelmioApiDocBundle/blob/master/Formatter/AbstractFormatter.php#L103)

This is possible by the addition two properties to each property item in
`response`, and `parameters` fields in each API endpoint produced by the
`ApiDocExtractor`:

* `actualType` Contains a value from one of the `DataTypes` class constants.

* `subType` Can contain either `null`, or any other `DataTypes` class constant.
This is relevant when the `actualType` is a `DataTypes::COLLECTION`, wherein
`subType` would specify the type of the collection items. It is also relevant
when `actualType` is a `DataTypes::MODEL`, wherein `subType` would contain an
identifier of the model (the FQCN or anything the parser would wish to specify)

Examples:

```php

array(
    'id' => array(
        'dataType' => 'integer',
        'actualType' => DataTypes::INTEGER,
        'subType' => null,
    ),
    'profile' => array(
        'dataType' => 'object (Profile)',
        'actualType' => DataTypes::MODEL,
        'subType' => 'Foo\Entity\Profile',
        'children' => array(
            'name' => array(
                'dataType' => 'string',
                'actualType' => DataTypes::STRING,
                'subType' => null,
             ),
            'birthDate' => array(
                'dataType' => 'date',
                'actualType' => DataTypes::DATE,
                'subType' => null,
            ),
        )
    ),
    'languages' => array(
        'dataType' => 'array of strings',
        'actualType' => DataTypes::COLLECTION,
        'subType' => DataTypes::STRING,
    ),
    'roles' => array(
        'dataType' => 'array of choices',
        'actualType' => DataTypes::COLLECTION,
        'subType' => DataTypes::ENUM,
    ),
    'groups' => array(
        'dataType' => 'array of objects (Group)',
        'actualType' => DataTypes::COLLECTION,
        'subType' => 'Foo\Entity\Group',
    ),
    'profileRevisions' => array(
         'dataType' => 'array of objects (Profile)',
         'actualType' => DataTypes::COLLECTION,
         'subType' => 'Foo\Entity\Profile',
    ),
    'address' => array(
        'dataType' => 'object (a_type_a_custom_JMS_serializer_handler_handles)',
        'actualType' => DataTypes::MODEL,
        'subType' => 'a_type_a_custom_JMS_serializer_handler_handles',
    ),
);
```

When a formatter omits the `dataType` property or leaves it blank, it is
inferred within `ApiDocExtractor` before everything is passed to formatters.
This commit is contained in:
Bez Hermoso 2014-06-17 17:05:00 -07:00 committed by William DURAND
parent b48650a9e0
commit 14d1021c8b
11 changed files with 467 additions and 44 deletions

63
DataTypes.php Normal file
View File

@ -0,0 +1,63 @@
<?php
/*
* This file is part of the NelmioApiDocBundle.
*
* (c) Nelmio <hello@nelm.io>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Nelmio\ApiDocBundle;
/**
* All the supported data-types which will be specified in the `actualType` properties in parameters.
*
* @author Bez Hermoso <bez@activelamp.com>
*/
class DataTypes
{
const INTEGER = 'integer';
const FLOAT = 'float';
const STRING = 'string';
const BOOLEAN = 'boolean';
const FILE = 'file';
const ENUM = 'choice';
const COLLECTION = 'collection';
const MODEL = 'model';
const DATE = 'date';
const DATETIME = 'datetime';
const TIME = 'time';
/**
* Returns true if the supplied `actualType` value is considered a primitive type. Returns false, otherwise.
*
* @param string $type
* @return bool
*/
public static function isPrimitive($type)
{
return in_array(strtolower($type), array(
static::INTEGER,
static::FLOAT,
static::STRING,
static::BOOLEAN,
static::FILE,
static::DATE,
static::DATETIME,
static::TIME,
static::ENUM,
));
}
}

View File

@ -14,6 +14,7 @@ namespace Nelmio\ApiDocBundle\Extractor;
use Doctrine\Common\Annotations\Reader; use Doctrine\Common\Annotations\Reader;
use Doctrine\Common\Util\ClassUtils; use Doctrine\Common\Util\ClassUtils;
use Nelmio\ApiDocBundle\Annotation\ApiDoc; use Nelmio\ApiDocBundle\Annotation\ApiDoc;
use Nelmio\ApiDocBundle\DataTypes;
use Nelmio\ApiDocBundle\Parser\ParserInterface; use Nelmio\ApiDocBundle\Parser\ParserInterface;
use Nelmio\ApiDocBundle\Parser\PostParserInterface; use Nelmio\ApiDocBundle\Parser\PostParserInterface;
use Symfony\Component\Routing\Route; use Symfony\Component\Routing\Route;
@ -279,6 +280,7 @@ class ApiDocExtractor
} }
$parameters = $this->clearClasses($parameters); $parameters = $this->clearClasses($parameters);
$parameters = $this->generateHumanReadableTypes($parameters);
if ('PUT' === $method) { if ('PUT' === $method) {
// All parameters are optional with PUT (update) // All parameters are optional with PUT (update)
@ -302,6 +304,7 @@ class ApiDocExtractor
} }
$response = $this->clearClasses($response); $response = $this->clearClasses($response);
$response = $this->generateHumanReadableTypes($response);
$annotation->setResponse($response); $annotation->setResponse($response);
} }
@ -414,6 +417,61 @@ class ApiDocExtractor
return $array; return $array;
} }
/**
* Populates the `dataType` properties in the parameter array if empty. Recurses through children when necessary.
*
* @param array $array
* @return array
*/
protected function generateHumanReadableTypes(array $array)
{
foreach ($array as $name => $info) {
if (empty($info['dataType'])) {
$array[$name]['dataType'] = $this->generateHumanReadableType($info['actualType'], $info['subType']);
}
if (isset($info['children'])) {
$array[$name]['children'] = $this->generateHumanReadableTypes($info['children']);
}
}
return $array;
}
/**
* Creates a human-readable version of the `actualType`. `subType` is taken into account.
*
* @param string $actualType
* @param string $subType
* @return string
*/
protected function generateHumanReadableType($actualType, $subType)
{
if ($actualType == DataTypes::MODEL) {
$parts = explode('\\', $subType);
return sprintf('object (%s)', end($parts));
}
if ($actualType == DataTypes::COLLECTION) {
if (DataTypes::isPrimitive($subType)) {
return sprintf('array of %ss', $subType);
}
if (class_exists($subType)) {
$parts = explode('\\', $subType);
return sprintf('array of objects (%s)', end($parts));
}
return sprintf('array of objects (%s)', $subType);
}
return $actualType;
}
private function getParsers(array $parameters) private function getParsers(array $parameters)
{ {
if (isset($parameters['parsers'])) { if (isset($parameters['parsers'])) {

View File

@ -11,6 +11,7 @@
namespace Nelmio\ApiDocBundle\Extractor\Handler; namespace Nelmio\ApiDocBundle\Extractor\Handler;
use Nelmio\ApiDocBundle\DataTypes;
use Nelmio\ApiDocBundle\Extractor\HandlerInterface; use Nelmio\ApiDocBundle\Extractor\HandlerInterface;
use Nelmio\ApiDocBundle\Annotation\ApiDoc; use Nelmio\ApiDocBundle\Annotation\ApiDoc;
use Symfony\Component\Routing\Route; use Symfony\Component\Routing\Route;
@ -28,9 +29,13 @@ class FosRestHandler implements HandlerInterface
{ {
foreach ($annotations as $annot) { foreach ($annotations as $annot) {
if ($annot instanceof RequestParam) { if ($annot instanceof RequestParam) {
$requirements = $this->handleRequirements($annot->requirements);
$annotation->addParameter($annot->name, array( $annotation->addParameter($annot->name, array(
'required' => $annot->strict && $annot->default === null, 'required' => $annot->strict && $annot->default === null,
'dataType' => $this->handleRequirements($annot->requirements), 'dataType' => $requirements,
'actualType' => $this->inferType($requirements),
'subType' => null,
'description' => $annot->description, 'description' => $annot->description,
'readonly' => false 'readonly' => false
)); ));
@ -76,4 +81,13 @@ class FosRestHandler implements HandlerInterface
return (string) $requirements; return (string) $requirements;
} }
public function inferType($requirement)
{
if (DataTypes::isPrimitive($requirement)) {
return $requirement;
}
return DataTypes::STRING;
}
} }

View File

@ -12,6 +12,7 @@
namespace Nelmio\ApiDocBundle\Formatter; namespace Nelmio\ApiDocBundle\Formatter;
use Nelmio\ApiDocBundle\Annotation\ApiDoc; use Nelmio\ApiDocBundle\Annotation\ApiDoc;
use Nelmio\ApiDocBundle\DataTypes;
abstract class AbstractFormatter implements FormatterInterface abstract class AbstractFormatter implements FormatterInterface
{ {
@ -76,6 +77,8 @@ abstract class AbstractFormatter implements FormatterInterface
'format' => array_key_exists('format', $info) ? $info['format'] : null, 'format' => array_key_exists('format', $info) ? $info['format'] : null,
'sinceVersion' => array_key_exists('sinceVersion', $info) ? $info['sinceVersion'] : null, 'sinceVersion' => array_key_exists('sinceVersion', $info) ? $info['sinceVersion'] : null,
'untilVersion' => array_key_exists('untilVersion', $info) ? $info['untilVersion'] : null, 'untilVersion' => array_key_exists('untilVersion', $info) ? $info['untilVersion'] : null,
'actualType' => array_key_exists('actualType', $info) ? $info['actualType'] : null,
'subType' => array_key_exists('subType', $info) ? $info['subType'] : null,
); );
if (isset($info['children']) && (!$info['readonly'] || !$ignoreNestedReadOnly)) { if (isset($info['children']) && (!$info['readonly'] || !$ignoreNestedReadOnly)) {
@ -100,7 +103,7 @@ abstract class AbstractFormatter implements FormatterInterface
protected function getNewName($name, $data, $parentName = null) protected function getNewName($name, $data, $parentName = null)
{ {
$newName = ($parentName) ? sprintf("%s[%s]", $parentName, $name) : $name; $newName = ($parentName) ? sprintf("%s[%s]", $parentName, $name) : $name;
$array = (false === strpos($data['dataType'], "array of")) ? "" : "[]"; $array = $data['actualType'] == DataTypes::COLLECTION && $data['subType'] !== null ? '[]' : '';
return sprintf("%s%s", $newName, $array); return sprintf("%s%s", $newName, $array);
} }

View File

@ -11,6 +11,7 @@
namespace Nelmio\ApiDocBundle\Parser; namespace Nelmio\ApiDocBundle\Parser;
use Nelmio\ApiDocBundle\DataTypes;
use Symfony\Component\Form\Exception\UnexpectedTypeException; use Symfony\Component\Form\Exception\UnexpectedTypeException;
use Symfony\Component\Form\Exception\InvalidArgumentException; use Symfony\Component\Form\Exception\InvalidArgumentException;
use Symfony\Component\Form\Extension\Core\ChoiceList\ChoiceListInterface; use Symfony\Component\Form\Extension\Core\ChoiceList\ChoiceListInterface;
@ -35,16 +36,16 @@ class FormTypeParser implements ParserInterface
* @var array * @var array
*/ */
protected $mapTypes = array( protected $mapTypes = array(
'text' => 'string', 'text' => DataTypes::STRING,
'date' => 'date', 'date' => DataTypes::DATE,
'datetime' => 'datetime', 'datetime' => DataTypes::DATETIME,
'checkbox' => 'boolean', 'checkbox' => DataTypes::BOOLEAN,
'time' => 'time', 'time' => DataTypes::TIME,
'number' => 'float', 'number' => DataTypes::FLOAT,
'integer' => 'int', 'integer' => DataTypes::INTEGER,
'textarea' => 'string', 'textarea' => DataTypes::STRING,
'country' => 'string', 'country' => DataTypes::STRING,
'choice' => 'choice', 'choice' => DataTypes::ENUM,
); );
public function __construct(FormFactoryInterface $formFactory) public function __construct(FormFactoryInterface $formFactory)
@ -99,12 +100,18 @@ class FormTypeParser implements ParserInterface
} }
$bestType = ''; $bestType = '';
$actualType = null;
$subType = null;
for ($type = $config->getType(); null !== $type; $type = $type->getParent()) { for ($type = $config->getType(); null !== $type; $type = $type->getParent()) {
if (isset($this->mapTypes[$type->getName()])) { if (isset($this->mapTypes[$type->getName()])) {
$bestType = $this->mapTypes[$type->getName()]; $bestType = $this->mapTypes[$type->getName()];
$actualType = $bestType;
} elseif ('collection' === $type->getName()) { } elseif ('collection' === $type->getName()) {
if (is_string($config->getOption('type')) && isset($this->mapTypes[$config->getOption('type')])) { if (is_string($config->getOption('type')) && isset($this->mapTypes[$config->getOption('type')])) {
$bestType = sprintf('array of %ss', $this->mapTypes[$config->getOption('type')]); $subType = $this->mapTypes[$config->getOption('type')];
$actualType = DataTypes::COLLECTION;
$bestType = sprintf('array of %ss', $subType);
} else { } else {
// Embedded form collection // Embedded form collection
$subParameters = $this->parseForm($this->formFactory->create($config->getOption('type'), null, $config->getOption('options', array())), $name . '[]'); $subParameters = $this->parseForm($this->formFactory->create($config->getOption('type'), null, $config->getOption('options', array())), $name . '[]');
@ -139,6 +146,7 @@ class FormTypeParser implements ParserInterface
if ($addDefault) { if ($addDefault) {
$parameters[$name] = array( $parameters[$name] = array(
'dataType' => 'string', 'dataType' => 'string',
'actualType' => 'string',
'required' => $config->getRequired(), 'required' => $config->getRequired(),
'description' => $config->getAttribute('description'), 'description' => $config->getAttribute('description'),
'readonly' => $config->getDisabled(), 'readonly' => $config->getDisabled(),
@ -152,13 +160,15 @@ class FormTypeParser implements ParserInterface
$parameters[$name] = array( $parameters[$name] = array(
'dataType' => $bestType, 'dataType' => $bestType,
'actualType' => $actualType,
'subType' => $subType,
'required' => $config->getRequired(), 'required' => $config->getRequired(),
'description' => $config->getAttribute('description'), 'description' => $config->getAttribute('description'),
'readonly' => $config->getDisabled(), 'readonly' => $config->getDisabled(),
); );
switch ($bestType) { switch ($actualType) {
case 'datetime': case DataTypes::DATETIME:
if (($format = $config->getOption('date_format')) && is_string($format)) { if (($format = $config->getOption('date_format')) && is_string($format)) {
$parameters[$name]['format'] = $format; $parameters[$name]['format'] = $format;
} elseif ('single_text' == $config->getOption('widget') && $format = $config->getOption('format')) { } elseif ('single_text' == $config->getOption('widget') && $format = $config->getOption('format')) {
@ -166,15 +176,17 @@ class FormTypeParser implements ParserInterface
} }
break; break;
case 'date': case DataTypes::DATE:
if (($format = $config->getOption('format')) && is_string($format)) { if (($format = $config->getOption('format')) && is_string($format)) {
$parameters[$name]['format'] = $format; $parameters[$name]['format'] = $format;
} }
break; break;
case 'choice': case DataTypes::ENUM:
if ($config->getOption('multiple')) { if ($config->getOption('multiple')) {
$parameters[$name]['dataType'] = sprintf('array of %ss', $parameters[$name]['dataType']); $parameters[$name]['dataType'] = sprintf('array of %ss', $parameters[$name]['dataType']);
$parameters[$name]['actualType'] = DataTypes::COLLECTION;
$parameters[$name]['subType'] = DataTypes::ENUM;
} }
if (($choices = $config->getOption('choices')) && is_array($choices) && count($choices)) { if (($choices = $config->getOption('choices')) && is_array($choices) && count($choices)) {

View File

@ -14,6 +14,7 @@ namespace Nelmio\ApiDocBundle\Parser;
use JMS\Serializer\Exclusion\GroupsExclusionStrategy; use JMS\Serializer\Exclusion\GroupsExclusionStrategy;
use JMS\Serializer\SerializationContext; use JMS\Serializer\SerializationContext;
use Metadata\MetadataFactoryInterface; use Metadata\MetadataFactoryInterface;
use Nelmio\ApiDocBundle\DataTypes;
use Nelmio\ApiDocBundle\Util\DocCommentExtractor; use Nelmio\ApiDocBundle\Util\DocCommentExtractor;
use JMS\Serializer\Metadata\PropertyMetadata; use JMS\Serializer\Metadata\PropertyMetadata;
use JMS\Serializer\Metadata\VirtualPropertyMetadata; use JMS\Serializer\Metadata\VirtualPropertyMetadata;
@ -39,6 +40,15 @@ class JmsMetadataParser implements ParserInterface
*/ */
private $commentExtractor; private $commentExtractor;
private $typeMap = array(
'integer' => DataTypes::INTEGER,
'boolean' => DataTypes::BOOLEAN,
'string' => DataTypes::STRING,
'float' => DataTypes::FLOAT,
'double' => DataTypes::FLOAT,
'array' => DataTypes::COLLECTION,
'DateTime' => DataTypes::DATETIME,
);
/** /**
* Constructor, requires JMS Metadata factory * Constructor, requires JMS Metadata factory
*/ */
@ -120,6 +130,8 @@ class JmsMetadataParser implements ParserInterface
$params[$name] = array( $params[$name] = array(
'dataType' => $dataType['normalized'], 'dataType' => $dataType['normalized'],
'actualType' => $dataType['actualType'],
'subType' => $dataType['class'],
'required' => false, 'required' => false,
//TODO: can't think of a good way to specify this one, JMS doesn't have a setting for this //TODO: can't think of a good way to specify this one, JMS doesn't have a setting for this
'description' => $this->getDescription($item), 'description' => $this->getDescription($item),
@ -128,7 +140,7 @@ class JmsMetadataParser implements ParserInterface
'untilVersion' => $item->untilVersion, 'untilVersion' => $item->untilVersion,
); );
if (!is_null($dataType['class'])) { if (!is_null($dataType['class']) && false === $dataType['primitive']) {
$params[$name]['class'] = $dataType['class']; $params[$name]['class'] = $dataType['class'];
} }
@ -138,7 +150,7 @@ class JmsMetadataParser implements ParserInterface
} }
// check for nested classes with JMS metadata // check for nested classes with JMS metadata
if ($dataType['class'] && null !== $this->factory->getMetadataForClass($dataType['class'])) { if ($dataType['class'] && false === $dataType['primitive'] && null !== $this->factory->getMetadataForClass($dataType['class'])) {
$visited[] = $dataType['class']; $visited[] = $dataType['class'];
$params[$name]['children'] = $this->doParse($dataType['class'], $visited, $groups); $params[$name]['children'] = $this->doParse($dataType['class'], $visited, $groups);
} }
@ -162,7 +174,9 @@ class JmsMetadataParser implements ParserInterface
if ($this->isPrimitive($nestedType)) { if ($this->isPrimitive($nestedType)) {
return array( return array(
'normalized' => sprintf("array of %ss", $nestedType), 'normalized' => sprintf("array of %ss", $nestedType),
'class' => null 'actualType' => DataTypes::COLLECTION,
'class' => $this->typeMap[$nestedType],
'primitive' => true,
); );
} }
@ -170,7 +184,9 @@ class JmsMetadataParser implements ParserInterface
return array( return array(
'normalized' => sprintf("array of objects (%s)", end($exp)), 'normalized' => sprintf("array of objects (%s)", end($exp)),
'class' => $nestedType 'actualType' => DataTypes::COLLECTION,
'class' => $nestedType,
'primitive' => false,
); );
} }
@ -180,7 +196,9 @@ class JmsMetadataParser implements ParserInterface
if ($this->isPrimitive($type)) { if ($this->isPrimitive($type)) {
return array( return array(
'normalized' => $type, 'normalized' => $type,
'class' => null 'actualType' => $this->typeMap[$type],
'class' => null,
'primitive' => true,
); );
} }
@ -188,7 +206,9 @@ class JmsMetadataParser implements ParserInterface
if (!class_exists($type)) { if (!class_exists($type)) {
return array( return array(
'normalized' => sprintf("custom handler result for (%s)", $type), 'normalized' => sprintf("custom handler result for (%s)", $type),
'class' => null 'class' => $type,
'actualType' => DataTypes::MODEL,
'primitive' => false,
); );
} }
@ -197,7 +217,9 @@ class JmsMetadataParser implements ParserInterface
return array( return array(
'normalized' => sprintf("object (%s)", end($exp)), 'normalized' => sprintf("object (%s)", end($exp)),
'class' => $type 'class' => $type,
'actualType' => DataTypes::MODEL,
'primitive' => false,
); );
} }

View File

@ -11,6 +11,7 @@
namespace Nelmio\ApiDocBundle\Parser; namespace Nelmio\ApiDocBundle\Parser;
use Nelmio\ApiDocBundle\DataTypes;
use Symfony\Component\Validator\Exception\ConstraintDefinitionException; use Symfony\Component\Validator\Exception\ConstraintDefinitionException;
use Symfony\Component\Validator\MetadataFactoryInterface; use Symfony\Component\Validator\MetadataFactoryInterface;
use Symfony\Component\Validator\Constraint; use Symfony\Component\Validator\Constraint;
@ -26,6 +27,21 @@ class ValidationParser implements ParserInterface, PostParserInterface
*/ */
protected $factory; protected $factory;
protected $typeMap = array(
'integer' => DataTypes::INTEGER,
'int' => DataTypes::INTEGER,
'scalar' => DataTypes::STRING,
'numeric' => DataTypes::INTEGER,
'boolean' => DataTypes::BOOLEAN,
'string' => DataTypes::STRING,
'float' => DataTypes::FLOAT,
'double' => DataTypes::FLOAT,
'long' => DataTypes::INTEGER,
'object' => DataTypes::MODEL,
'array' => DataTypes::COLLECTION,
'DateTime' => DataTypes::DATETIME,
);
/** /**
* Requires a validation MetadataFactory. * Requires a validation MetadataFactory.
* *
@ -142,6 +158,9 @@ class ValidationParser implements ParserInterface, PostParserInterface
{ {
$class = substr(get_class($constraint), strlen('Symfony\\Component\\Validator\\Constraints\\')); $class = substr(get_class($constraint), strlen('Symfony\\Component\\Validator\\Constraints\\'));
$vparams['actualType'] = DataTypes::STRING;
$vparams['subType'] = null;
switch ($class) { switch ($class) {
case 'NotBlank': case 'NotBlank':
$vparams['format'][] = '{not blank}'; $vparams['format'][] = '{not blank}';
@ -149,6 +168,9 @@ class ValidationParser implements ParserInterface, PostParserInterface
$vparams['required'] = true; $vparams['required'] = true;
break; break;
case 'Type': case 'Type':
if (isset($this->typeMap[$constraint->type])) {
$vparams['actualType'] = $this->typeMap[$constraint->type];
}
$vparams['dataType'] = $constraint->type; $vparams['dataType'] = $constraint->type;
break; break;
case 'Email': case 'Email':
@ -162,12 +184,15 @@ class ValidationParser implements ParserInterface, PostParserInterface
break; break;
case 'Date': case 'Date':
$vparams['format'][] = '{Date YYYY-MM-DD}'; $vparams['format'][] = '{Date YYYY-MM-DD}';
$vparams['actualType'] = DataTypes::DATE;
break; break;
case 'DateTime': case 'DateTime':
$vparams['format'][] = '{DateTime YYYY-MM-DD HH:MM:SS}'; $vparams['format'][] = '{DateTime YYYY-MM-DD HH:MM:SS}';
$vparams['actualType'] = DataTypes::DATETIME;
break; break;
case 'Time': case 'Time':
$vparams['format'][] = '{Time HH:MM:SS}'; $vparams['format'][] = '{Time HH:MM:SS}';
$vparams['actualType'] = DataTypes::TIME;
break; break;
case 'Length': case 'Length':
$messages = array(); $messages = array();
@ -183,6 +208,8 @@ class ValidationParser implements ParserInterface, PostParserInterface
$choices = $this->getChoices($constraint, $className); $choices = $this->getChoices($constraint, $className);
$format = '[' . join('|', $choices) . ']'; $format = '[' . join('|', $choices) . ']';
if ($constraint->multiple) { if ($constraint->multiple) {
$vparams['actualType'] = DataTypes::COLLECTION;
$vparams['subType'] = DataTypes::ENUM;
$messages = array(); $messages = array();
if (isset($constraint->min)) { if (isset($constraint->min)) {
$messages[] = "min: {$constraint->min} "; $messages[] = "min: {$constraint->min} ";
@ -192,6 +219,7 @@ class ValidationParser implements ParserInterface, PostParserInterface
} }
$vparams['format'][] = '{' . join ('', $messages) . 'choice of ' . $format . '}'; $vparams['format'][] = '{' . join ('', $messages) . 'choice of ' . $format . '}';
} else { } else {
$vparams['actualType'] = DataTypes::ENUM;
$vparams['format'][] = $format; $vparams['format'][] = $format;
} }
break; break;
@ -216,6 +244,8 @@ class ValidationParser implements ParserInterface, PostParserInterface
} }
$vparams['dataType'] = sprintf("array of objects (%s)", end($exp)); $vparams['dataType'] = sprintf("array of objects (%s)", end($exp));
$vparams['actualType'] = DataTypes::COLLECTION;
$vparams['subType'] = $nestedType;
$vparams['class'] = $nestedType; $vparams['class'] = $nestedType;
if (!in_array($nestedType, $visited)) { if (!in_array($nestedType, $visited)) {

View File

@ -11,6 +11,7 @@
namespace Nelmio\ApiDocBundle\Tests\Formatter; namespace Nelmio\ApiDocBundle\Tests\Formatter;
use Nelmio\ApiDocBundle\DataTypes;
use Nelmio\ApiDocBundle\Tests\WebTestCase; use Nelmio\ApiDocBundle\Tests\WebTestCase;
class SimpleFormatterTest extends WebTestCase class SimpleFormatterTest extends WebTestCase
@ -108,18 +109,24 @@ class SimpleFormatterTest extends WebTestCase
array( array(
'a' => array( 'a' => array(
'dataType' => 'string', 'dataType' => 'string',
'actualType' => DataTypes::STRING,
'subType' => null,
'required' => true, 'required' => true,
'description' => 'A nice description', 'description' => 'A nice description',
'readonly' => false, 'readonly' => false,
), ),
'b' => array( 'b' => array(
'dataType' => 'string', 'dataType' => 'string',
'actualType' => DataTypes::STRING,
'subType' => null,
'required' => false, 'required' => false,
'description' => '', 'description' => '',
'readonly' => false, 'readonly' => false,
), ),
'c' => array( 'c' => array(
'dataType' => 'boolean', 'dataType' => 'boolean',
'actualType' => DataTypes::BOOLEAN,
'subType' => null,
'required' => true, 'required' => true,
'description' => '', 'description' => '',
'readonly' => false, 'readonly' => false,
@ -149,18 +156,24 @@ class SimpleFormatterTest extends WebTestCase
array( array(
'a' => array( 'a' => array(
'dataType' => 'string', 'dataType' => 'string',
'actualType' => DataTypes::STRING,
'subType' => null,
'required' => true, 'required' => true,
'description' => 'A nice description', 'description' => 'A nice description',
'readonly' => false, 'readonly' => false,
), ),
'b' => array( 'b' => array(
'dataType' => 'string', 'dataType' => 'string',
'actualType' => DataTypes::STRING,
'subType' => null,
'required' => false, 'required' => false,
'description' => '', 'description' => '',
'readonly' => false, 'readonly' => false,
), ),
'c' => array( 'c' => array(
'dataType' => 'boolean', 'dataType' => 'boolean',
'actualType' => DataTypes::BOOLEAN,
'subType' => null,
'required' => true, 'required' => true,
'description' => '', 'description' => '',
'readonly' => false, 'readonly' => false,
@ -193,6 +206,8 @@ class SimpleFormatterTest extends WebTestCase
'dependency_type[a]' => 'dependency_type[a]' =>
array( array(
'dataType' => 'string', 'dataType' => 'string',
'actualType' => DataTypes::STRING,
'subType' => null,
'required' => true, 'required' => true,
'description' => 'A nice description', 'description' => 'A nice description',
'readonly' => false, 'readonly' => false,
@ -251,6 +266,8 @@ class SimpleFormatterTest extends WebTestCase
'foo' => 'foo' =>
array( array(
'dataType' => 'string', 'dataType' => 'string',
'actualType' => DataTypes::STRING,
'subType' => null,
'required' => false, 'required' => false,
'description' => '', 'description' => '',
'readonly' => false, 'readonly' => false,
@ -260,6 +277,8 @@ class SimpleFormatterTest extends WebTestCase
'bar' => 'bar' =>
array( array(
'dataType' => 'DateTime', 'dataType' => 'DateTime',
'actualType' => DataTypes::DATETIME,
'subType' => null,
'required' => false, 'required' => false,
'description' => '', 'description' => '',
'readonly' => true, 'readonly' => true,
@ -269,6 +288,8 @@ class SimpleFormatterTest extends WebTestCase
'number' => 'number' =>
array( array(
'dataType' => 'double', 'dataType' => 'double',
'actualType' => DataTypes::FLOAT,
'subType' => null,
'required' => false, 'required' => false,
'description' => '', 'description' => '',
'readonly' => false, 'readonly' => false,
@ -278,6 +299,8 @@ class SimpleFormatterTest extends WebTestCase
'arr' => 'arr' =>
array( array(
'dataType' => 'array', 'dataType' => 'array',
'actualType' => DataTypes::COLLECTION,
'subType' => null,
'required' => false, 'required' => false,
'description' => '', 'description' => '',
'readonly' => false, 'readonly' => false,
@ -287,6 +310,8 @@ class SimpleFormatterTest extends WebTestCase
'nested' => 'nested' =>
array( array(
'dataType' => 'object (JmsNested)', 'dataType' => 'object (JmsNested)',
'actualType' => DataTypes::MODEL,
'subType' => 'Nelmio\ApiDocBundle\Tests\Fixtures\Model\JmsNested',
'required' => false, 'required' => false,
'description' => '', 'description' => '',
'readonly' => false, 'readonly' => false,
@ -297,6 +322,8 @@ class SimpleFormatterTest extends WebTestCase
'foo' => 'foo' =>
array( array(
'dataType' => 'DateTime', 'dataType' => 'DateTime',
'actualType' => DataTypes::DATETIME,
'subType' => null,
'required' => false, 'required' => false,
'description' => '', 'description' => '',
'readonly' => true, 'readonly' => true,
@ -306,6 +333,8 @@ class SimpleFormatterTest extends WebTestCase
'bar' => 'bar' =>
array( array(
'dataType' => 'string', 'dataType' => 'string',
'actualType' => DataTypes::STRING,
'subType' => null,
'required' => false, 'required' => false,
'description' => '', 'description' => '',
'readonly' => false, 'readonly' => false,
@ -315,6 +344,8 @@ class SimpleFormatterTest extends WebTestCase
'baz' => 'baz' =>
array( array(
'dataType' => 'array of integers', 'dataType' => 'array of integers',
'actualType' => DataTypes::COLLECTION,
'subType' => DataTypes::INTEGER,
'required' => false, 'required' => false,
'description' => 'Epic description. 'description' => 'Epic description.
@ -326,6 +357,8 @@ With multiple lines.',
'circular' => 'circular' =>
array( array(
'dataType' => 'object (JmsNested)', 'dataType' => 'object (JmsNested)',
'actualType' => DataTypes::MODEL,
'subType' => 'Nelmio\ApiDocBundle\Tests\Fixtures\Model\JmsNested',
'required' => false, 'required' => false,
'description' => '', 'description' => '',
'readonly' => false, 'readonly' => false,
@ -335,6 +368,8 @@ With multiple lines.',
'parent' => 'parent' =>
array( array(
'dataType' => 'object (JmsTest)', 'dataType' => 'object (JmsTest)',
'actualType' => DataTypes::MODEL,
'subType' => 'Nelmio\ApiDocBundle\Tests\Fixtures\Model\JmsTest',
'required' => false, 'required' => false,
'description' => '', 'description' => '',
'readonly' => false, 'readonly' => false,
@ -345,6 +380,8 @@ With multiple lines.',
'foo' => 'foo' =>
array( array(
'dataType' => 'string', 'dataType' => 'string',
'actualType' => DataTypes::STRING,
'subType' => null,
'required' => false, 'required' => false,
'description' => '', 'description' => '',
'readonly' => false, 'readonly' => false,
@ -354,6 +391,8 @@ With multiple lines.',
'bar' => 'bar' =>
array( array(
'dataType' => 'DateTime', 'dataType' => 'DateTime',
'actualType' => DataTypes::DATETIME,
'subType' => null,
'required' => false, 'required' => false,
'description' => '', 'description' => '',
'readonly' => true, 'readonly' => true,
@ -363,6 +402,8 @@ With multiple lines.',
'number' => 'number' =>
array( array(
'dataType' => 'double', 'dataType' => 'double',
'actualType' => DataTypes::FLOAT,
'subType' => null,
'required' => false, 'required' => false,
'description' => '', 'description' => '',
'readonly' => false, 'readonly' => false,
@ -372,6 +413,8 @@ With multiple lines.',
'arr' => 'arr' =>
array( array(
'dataType' => 'array', 'dataType' => 'array',
'actualType' => DataTypes::COLLECTION,
'subType' => null,
'required' => false, 'required' => false,
'description' => '', 'description' => '',
'readonly' => false, 'readonly' => false,
@ -381,6 +424,8 @@ With multiple lines.',
'nested' => 'nested' =>
array( array(
'dataType' => 'object (JmsNested)', 'dataType' => 'object (JmsNested)',
'actualType' => DataTypes::MODEL,
'subType' => 'Nelmio\ApiDocBundle\Tests\Fixtures\Model\JmsNested',
'required' => false, 'required' => false,
'description' => '', 'description' => '',
'readonly' => false, 'readonly' => false,
@ -390,6 +435,8 @@ With multiple lines.',
'nested_array' => 'nested_array' =>
array( array(
'dataType' => 'array of objects (JmsNested)', 'dataType' => 'array of objects (JmsNested)',
'actualType' => DataTypes::COLLECTION,
'subType' => 'Nelmio\ApiDocBundle\Tests\Fixtures\Model\JmsNested',
'required' => false, 'required' => false,
'description' => '', 'description' => '',
'readonly' => false, 'readonly' => false,
@ -401,6 +448,8 @@ With multiple lines.',
'since' => 'since' =>
array ( array (
'dataType' => 'string', 'dataType' => 'string',
'actualType' => DataTypes::STRING,
'subType' => null,
'required' => false, 'required' => false,
'description' => '', 'description' => '',
'readonly' => false, 'readonly' => false,
@ -410,6 +459,8 @@ With multiple lines.',
'until' => 'until' =>
array ( array (
'dataType' => 'string', 'dataType' => 'string',
'actualType' => DataTypes::STRING,
'subType' => null,
'required' => false, 'required' => false,
'description' => '', 'description' => '',
'readonly' => false, 'readonly' => false,
@ -419,6 +470,8 @@ With multiple lines.',
'since_and_until' => 'since_and_until' =>
array ( array (
'dataType' => 'string', 'dataType' => 'string',
'actualType' => DataTypes::STRING,
'subType' => null,
'required' => false, 'required' => false,
'description' => '', 'description' => '',
'readonly' => false, 'readonly' => false,
@ -430,6 +483,8 @@ With multiple lines.',
'nested_array' => 'nested_array' =>
array( array(
'dataType' => 'array of objects (JmsNested)', 'dataType' => 'array of objects (JmsNested)',
'actualType' => DataTypes::COLLECTION,
'subType' => 'Nelmio\ApiDocBundle\Tests\Fixtures\Model\JmsNested',
'required' => false, 'required' => false,
'description' => '', 'description' => '',
'readonly' => false, 'readonly' => false,
@ -452,6 +507,8 @@ With multiple lines.',
'dependency_type[a]' => 'dependency_type[a]' =>
array( array(
'dataType' => 'string', 'dataType' => 'string',
'actualType' => DataTypes::STRING,
'subType' => null,
'required' => true, 'required' => true,
'description' => 'A nice description', 'description' => 'A nice description',
'readonly' => false, 'readonly' => false,
@ -516,6 +573,8 @@ And, it supports multilines until the first \'@\' char.',
'foo' => 'foo' =>
array ( array (
'dataType' => 'string', 'dataType' => 'string',
'actualType' => DataTypes::STRING,
'subType' => null,
'required' => false, 'required' => false,
'description' => '', 'description' => '',
'readonly' => false, 'readonly' => false,
@ -525,6 +584,8 @@ And, it supports multilines until the first \'@\' char.',
'bar' => 'bar' =>
array ( array (
'dataType' => 'DateTime', 'dataType' => 'DateTime',
'actualType' => DataTypes::DATETIME,
'subType' => null,
'required' => false, 'required' => false,
'description' => '', 'description' => '',
'readonly' => true, 'readonly' => true,
@ -534,6 +595,8 @@ And, it supports multilines until the first \'@\' char.',
'number' => 'number' =>
array ( array (
'dataType' => 'double', 'dataType' => 'double',
'actualType' => DataTypes::FLOAT,
'subType' => null,
'required' => false, 'required' => false,
'description' => '', 'description' => '',
'readonly' => false, 'readonly' => false,
@ -543,6 +606,8 @@ And, it supports multilines until the first \'@\' char.',
'arr' => 'arr' =>
array ( array (
'dataType' => 'array', 'dataType' => 'array',
'actualType' => DataTypes::COLLECTION,
'subType' => null,
'required' => false, 'required' => false,
'description' => '', 'description' => '',
'readonly' => false, 'readonly' => false,
@ -552,6 +617,8 @@ And, it supports multilines until the first \'@\' char.',
'nested' => 'nested' =>
array ( array (
'dataType' => 'object (JmsNested)', 'dataType' => 'object (JmsNested)',
'actualType' => DataTypes::MODEL,
'subType' => 'Nelmio\ApiDocBundle\Tests\Fixtures\Model\JmsNested',
'required' => false, 'required' => false,
'description' => '', 'description' => '',
'readonly' => false, 'readonly' => false,
@ -562,6 +629,8 @@ And, it supports multilines until the first \'@\' char.',
'foo' => 'foo' =>
array ( array (
'dataType' => 'DateTime', 'dataType' => 'DateTime',
'actualType' => DataTypes::DATETIME,
'subType' => null,
'required' => false, 'required' => false,
'description' => '', 'description' => '',
'readonly' => true, 'readonly' => true,
@ -571,6 +640,8 @@ And, it supports multilines until the first \'@\' char.',
'bar' => 'bar' =>
array ( array (
'dataType' => 'string', 'dataType' => 'string',
'actualType' => DataTypes::STRING,
'subType' => null,
'required' => false, 'required' => false,
'description' => '', 'description' => '',
'readonly' => false, 'readonly' => false,
@ -580,6 +651,8 @@ And, it supports multilines until the first \'@\' char.',
'baz' => 'baz' =>
array ( array (
'dataType' => 'array of integers', 'dataType' => 'array of integers',
'actualType' => DataTypes::COLLECTION,
'subType' => DataTypes::INTEGER,
'required' => false, 'required' => false,
'description' => 'Epic description. 'description' => 'Epic description.
@ -591,6 +664,8 @@ With multiple lines.',
'circular' => 'circular' =>
array ( array (
'dataType' => 'object (JmsNested)', 'dataType' => 'object (JmsNested)',
'actualType' => DataTypes::MODEL,
'subType' => 'Nelmio\ApiDocBundle\Tests\Fixtures\Model\JmsNested',
'required' => false, 'required' => false,
'description' => '', 'description' => '',
'readonly' => false, 'readonly' => false,
@ -600,6 +675,8 @@ With multiple lines.',
'parent' => 'parent' =>
array ( array (
'dataType' => 'object (JmsTest)', 'dataType' => 'object (JmsTest)',
'actualType' => DataTypes::MODEL,
'subType' => 'Nelmio\ApiDocBundle\Tests\Fixtures\Model\JmsTest',
'required' => false, 'required' => false,
'description' => '', 'description' => '',
'readonly' => false, 'readonly' => false,
@ -610,6 +687,8 @@ With multiple lines.',
'foo' => 'foo' =>
array ( array (
'dataType' => 'string', 'dataType' => 'string',
'actualType' => DataTypes::STRING,
'subType' => null,
'required' => false, 'required' => false,
'description' => '', 'description' => '',
'readonly' => false, 'readonly' => false,
@ -619,6 +698,8 @@ With multiple lines.',
'bar' => 'bar' =>
array ( array (
'dataType' => 'DateTime', 'dataType' => 'DateTime',
'actualType' => DataTypes::DATETIME,
'subType' => null,
'required' => false, 'required' => false,
'description' => '', 'description' => '',
'readonly' => true, 'readonly' => true,
@ -628,6 +709,8 @@ With multiple lines.',
'number' => 'number' =>
array ( array (
'dataType' => 'double', 'dataType' => 'double',
'actualType' => DataTypes::FLOAT,
'subType' => null,
'required' => false, 'required' => false,
'description' => '', 'description' => '',
'readonly' => false, 'readonly' => false,
@ -637,6 +720,8 @@ With multiple lines.',
'arr' => 'arr' =>
array ( array (
'dataType' => 'array', 'dataType' => 'array',
'actualType' => DataTypes::COLLECTION,
'subType' => null,
'required' => false, 'required' => false,
'description' => '', 'description' => '',
'readonly' => false, 'readonly' => false,
@ -646,6 +731,8 @@ With multiple lines.',
'nested' => 'nested' =>
array ( array (
'dataType' => 'object (JmsNested)', 'dataType' => 'object (JmsNested)',
'actualType' => DataTypes::MODEL,
'subType' => 'Nelmio\ApiDocBundle\Tests\Fixtures\Model\JmsNested',
'required' => false, 'required' => false,
'description' => '', 'description' => '',
'readonly' => false, 'readonly' => false,
@ -655,6 +742,8 @@ With multiple lines.',
'nested_array' => 'nested_array' =>
array ( array (
'dataType' => 'array of objects (JmsNested)', 'dataType' => 'array of objects (JmsNested)',
'actualType' => DataTypes::COLLECTION,
'subType' => 'Nelmio\ApiDocBundle\Tests\Fixtures\Model\JmsNested',
'required' => false, 'required' => false,
'description' => '', 'description' => '',
'readonly' => false, 'readonly' => false,
@ -666,6 +755,8 @@ With multiple lines.',
'since' => 'since' =>
array ( array (
'dataType' => 'string', 'dataType' => 'string',
'actualType' => DataTypes::STRING,
'subType' => null,
'required' => false, 'required' => false,
'description' => '', 'description' => '',
'readonly' => false, 'readonly' => false,
@ -675,6 +766,8 @@ With multiple lines.',
'until' => 'until' =>
array ( array (
'dataType' => 'string', 'dataType' => 'string',
'actualType' => DataTypes::STRING,
'subType' => null,
'required' => false, 'required' => false,
'description' => '', 'description' => '',
'readonly' => false, 'readonly' => false,
@ -684,6 +777,8 @@ With multiple lines.',
'since_and_until' => 'since_and_until' =>
array ( array (
'dataType' => 'string', 'dataType' => 'string',
'actualType' => DataTypes::STRING,
'subType' => null,
'required' => false, 'required' => false,
'description' => '', 'description' => '',
'readonly' => false, 'readonly' => false,
@ -695,6 +790,8 @@ With multiple lines.',
'nested_array' => 'nested_array' =>
array ( array (
'dataType' => 'array of objects (JmsNested)', 'dataType' => 'array of objects (JmsNested)',
'actualType' => DataTypes::COLLECTION,
'subType' => 'Nelmio\ApiDocBundle\Tests\Fixtures\Model\JmsNested',
'required' => false, 'required' => false,
'description' => '', 'description' => '',
'readonly' => false, 'readonly' => false,
@ -802,6 +899,8 @@ With multiple lines.',
array( array(
'required' => true, 'required' => true,
'dataType' => 'string', 'dataType' => 'string',
'actualType' => DataTypes::STRING,
'subType' => null,
'description' => 'Param1 description.', 'description' => 'Param1 description.',
'readonly' => false, 'readonly' => false,
), ),
@ -821,11 +920,15 @@ With multiple lines.',
'response' => array ( 'response' => array (
'bar' => array( 'bar' => array(
'dataType' => 'DateTime', 'dataType' => 'DateTime',
'actualType' => DataTypes::DATETIME,
'subType' => null,
'required' => null, 'required' => null,
'readonly' => null 'readonly' => null
), ),
'number' => array( 'number' => array(
'dataType' => 'DateTime', 'dataType' => 'DateTime',
'actualType' => DataTypes::DATETIME,
'subType' => null,
'required' => false, 'required' => false,
'description' => '', 'description' => '',
'readonly' => false, 'readonly' => false,
@ -834,17 +937,23 @@ With multiple lines.',
), ),
'objects' => array( 'objects' => array(
'dataType' => 'array of objects (Test)', 'dataType' => 'array of objects (Test)',
'actualType' => DataTypes::COLLECTION,
'subType' => 'Nelmio\ApiDocBundle\Tests\Fixtures\Model\Test',
'readonly' => null, 'readonly' => null,
'required' => null, 'required' => null,
'children' => array( 'children' => array(
'a' => array( 'a' => array(
'dataType' => 'string', 'dataType' => 'string',
'actualType' => DataTypes::STRING,
'subType' => null,
'format' => '{length: min: foo}, {not blank}', 'format' => '{length: min: foo}, {not blank}',
'required' => true, 'required' => true,
'readonly' => null 'readonly' => null
), ),
'b' => array( 'b' => array(
'dataType' => 'DateTime', 'dataType' => 'DateTime',
'actualType' => DataTypes::DATETIME,
'subType' => null,
'required' => null, 'required' => null,
'readonly' => null 'readonly' => null
) )
@ -865,18 +974,24 @@ With multiple lines.',
array( array(
'a' => array( 'a' => array(
'dataType' => 'string', 'dataType' => 'string',
'actualType' => DataTypes::STRING,
'subType' => null,
'required' => true, 'required' => true,
'description' => 'A nice description', 'description' => 'A nice description',
'readonly' => false, 'readonly' => false,
), ),
'b' => array( 'b' => array(
'dataType' => 'string', 'dataType' => 'string',
'actualType' => DataTypes::STRING,
'subType' => null,
'required' => false, 'required' => false,
'description' => '', 'description' => '',
'readonly' => false, 'readonly' => false,
), ),
'c' => array( 'c' => array(
'dataType' => 'boolean', 'dataType' => 'boolean',
'actualType' => DataTypes::BOOLEAN,
'subType' => null,
'required' => true, 'required' => true,
'description' => '', 'description' => '',
'readonly' => false, 'readonly' => false,
@ -893,11 +1008,15 @@ With multiple lines.',
'response' => array ( 'response' => array (
'bar' => array( 'bar' => array(
'dataType' => 'DateTime', 'dataType' => 'DateTime',
'actualType' => DataTypes::DATETIME,
'subType' => null,
'required' => null, 'required' => null,
'readonly' => null 'readonly' => null
), ),
'number' => array( 'number' => array(
'dataType' => 'DateTime', 'dataType' => 'DateTime',
'actualType' => DataTypes::DATETIME,
'subType' => null,
'required' => false, 'required' => false,
'description' => '', 'description' => '',
'readonly' => false, 'readonly' => false,
@ -906,17 +1025,23 @@ With multiple lines.',
), ),
'objects' => array( 'objects' => array(
'dataType' => 'array of objects (Test)', 'dataType' => 'array of objects (Test)',
'actualType' => DataTypes::COLLECTION,
'subType' => 'Nelmio\ApiDocBundle\Tests\Fixtures\Model\Test',
'readonly' => null, 'readonly' => null,
'required' => null, 'required' => null,
'children' => array( 'children' => array(
'a' => array( 'a' => array(
'dataType' => 'string', 'dataType' => 'string',
'actualType' => DataTypes::STRING,
'subType' => null,
'format' => '{length: min: foo}, {not blank}', 'format' => '{length: min: foo}, {not blank}',
'required' => true, 'required' => true,
'readonly' => null 'readonly' => null
), ),
'b' => array( 'b' => array(
'dataType' => 'DateTime', 'dataType' => 'DateTime',
'actualType' => DataTypes::DATETIME,
'subType' => null,
'required' => null, 'required' => null,
'readonly' => null 'readonly' => null
) )

View File

@ -1,6 +1,7 @@
<?php <?php
namespace NelmioApiDocBundle\Tests\Parser; namespace NelmioApiDocBundle\Tests\Parser;
use Nelmio\ApiDocBundle\DataTypes;
use Nelmio\ApiDocBundle\Form\Extension\DescriptionFormTypeExtension; use Nelmio\ApiDocBundle\Form\Extension\DescriptionFormTypeExtension;
use Nelmio\ApiDocBundle\Parser\FormTypeParser; use Nelmio\ApiDocBundle\Parser\FormTypeParser;
use Nelmio\ApiDocBundle\Tests\Fixtures; use Nelmio\ApiDocBundle\Tests\Fixtures;
@ -37,18 +38,24 @@ class FormTypeParserTest extends \PHPUnit_Framework_TestCase
array( array(
'a' => array( 'a' => array(
'dataType' => 'string', 'dataType' => 'string',
'actualType' => DataTypes::STRING,
'subType' => null,
'required' => true, 'required' => true,
'description' => 'A nice description', 'description' => 'A nice description',
'readonly' => false 'readonly' => false
), ),
'b' => array( 'b' => array(
'dataType' => 'string', 'dataType' => 'string',
'actualType' => DataTypes::STRING,
'subType' => null,
'required' => true, 'required' => true,
'description' => '', 'description' => '',
'readonly' => false 'readonly' => false
), ),
'c' => array( 'c' => array(
'dataType' => 'boolean', 'dataType' => 'boolean',
'actualType' => DataTypes::BOOLEAN,
'subType' => null,
'required' => true, 'required' => true,
'description' => '', 'description' => '',
'readonly' => false 'readonly' => false
@ -60,24 +67,33 @@ class FormTypeParserTest extends \PHPUnit_Framework_TestCase
array( array(
'collection_type[a]' => array( 'collection_type[a]' => array(
'dataType' => 'array of strings', 'dataType' => 'array of strings',
'actualType' => DataTypes::COLLECTION,
'subType' => DataTypes::STRING,
'required' => true, 'required' => true,
'description' => '', 'description' => '',
'readonly' => false 'readonly' => false
), ),
'collection_type[b][][a]' => array( 'collection_type[b][][a]' => array(
'dataType' => 'string', 'dataType' => 'string',
'actualType' => DataTypes::STRING,
'subType' => null,
'required' => true, 'required' => true,
'description' => 'A nice description', 'description' => 'A nice description',
'readonly' => false 'readonly' => false
), ),
'collection_type[b][][b]' => array( 'collection_type[b][][b]' => array(
'dataType' => 'string', 'dataType' => 'string',
'actualType' => DataTypes::STRING,
'subType' => null,
'required' => true, 'required' => true,
'description' => '', 'description' => '',
'readonly' => false 'readonly' => false
), ),
'collection_type[b][][c]' => array( 'collection_type[b][][c]' => array(
'dataType' => 'boolean', 'dataType' => 'boolean',
'actualType' => DataTypes::BOOLEAN,
'subType' => null,
'required' => true, 'required' => true,
'description' => '', 'description' => '',
'readonly' => false 'readonly' => false
@ -92,24 +108,32 @@ class FormTypeParserTest extends \PHPUnit_Framework_TestCase
array( array(
'a' => array( 'a' => array(
'dataType' => 'array of strings', 'dataType' => 'array of strings',
'actualType' => DataTypes::COLLECTION,
'subType' => DataTypes::STRING,
'required' => true, 'required' => true,
'description' => '', 'description' => '',
'readonly' => false 'readonly' => false
), ),
'b[][a]' => array( 'b[][a]' => array(
'dataType' => 'string', 'dataType' => 'string',
'actualType' => DataTypes::STRING,
'subType' => null,
'required' => true, 'required' => true,
'description' => 'A nice description', 'description' => 'A nice description',
'readonly' => false 'readonly' => false
), ),
'b[][b]' => array( 'b[][b]' => array(
'dataType' => 'string', 'dataType' => 'string',
'actualType' => DataTypes::STRING,
'subType' => null,
'required' => true, 'required' => true,
'description' => '', 'description' => '',
'readonly' => false 'readonly' => false
), ),
'b[][c]' => array( 'b[][c]' => array(
'dataType' => 'boolean', 'dataType' => 'boolean',
'actualType' => DataTypes::BOOLEAN,
'subType' => null,
'required' => true, 'required' => true,
'description' => '', 'description' => '',
'readonly' => false 'readonly' => false
@ -124,24 +148,32 @@ class FormTypeParserTest extends \PHPUnit_Framework_TestCase
array( array(
'a' => array( 'a' => array(
'dataType' => 'array of strings', 'dataType' => 'array of strings',
'actualType' => DataTypes::COLLECTION,
'subType' => DataTypes::STRING,
'required' => true, 'required' => true,
'description' => '', 'description' => '',
'readonly' => false 'readonly' => false
), ),
'b[][a]' => array( 'b[][a]' => array(
'dataType' => 'string', 'dataType' => 'string',
'actualType' => DataTypes::STRING,
'subType' => null,
'required' => true, 'required' => true,
'description' => 'A nice description', 'description' => 'A nice description',
'readonly' => false 'readonly' => false
), ),
'b[][b]' => array( 'b[][b]' => array(
'dataType' => 'string', 'dataType' => 'string',
'actualType' => DataTypes::STRING,
'subType' => null,
'required' => true, 'required' => true,
'description' => '', 'description' => '',
'readonly' => false 'readonly' => false
), ),
'b[][c]' => array( 'b[][c]' => array(
'dataType' => 'boolean', 'dataType' => 'boolean',
'actualType' => DataTypes::BOOLEAN,
'subType' => null,
'required' => true, 'required' => true,
'description' => '', 'description' => '',
'readonly' => false 'readonly' => false
@ -153,6 +185,8 @@ class FormTypeParserTest extends \PHPUnit_Framework_TestCase
array( array(
'dt1' => array( 'dt1' => array(
'dataType' => 'datetime', 'dataType' => 'datetime',
'actualType' => DataTypes::DATETIME,
'subType' => null,
'required' => true, 'required' => true,
'description' => 'A nice description', 'description' => 'A nice description',
'readonly' => false, 'readonly' => false,
@ -160,6 +194,8 @@ class FormTypeParserTest extends \PHPUnit_Framework_TestCase
), ),
'dt2' => array( 'dt2' => array(
'dataType' => 'datetime', 'dataType' => 'datetime',
'actualType' => DataTypes::DATETIME,
'subType' => null,
'required' => true, 'required' => true,
'description' => '', 'description' => '',
'readonly' => false, 'readonly' => false,
@ -167,6 +203,8 @@ class FormTypeParserTest extends \PHPUnit_Framework_TestCase
), ),
'dt3' => array( 'dt3' => array(
'dataType' => 'datetime', 'dataType' => 'datetime',
'actualType' => DataTypes::DATETIME,
'subType' => null,
'required' => true, 'required' => true,
'description' => '', 'description' => '',
'readonly' => false, 'readonly' => false,
@ -174,24 +212,32 @@ class FormTypeParserTest extends \PHPUnit_Framework_TestCase
), ),
'dt4' => array( 'dt4' => array(
'dataType' => 'datetime', 'dataType' => 'datetime',
'actualType' => DataTypes::DATETIME,
'subType' => null,
'required' => true, 'required' => true,
'description' => '', 'description' => '',
'readonly' => false, 'readonly' => false,
), ),
'dt5' => array( 'dt5' => array(
'dataType' => 'datetime', 'dataType' => 'datetime',
'actualType' => DataTypes::DATETIME,
'subType' => null,
'required' => true, 'required' => true,
'description' => '', 'description' => '',
'readonly' => false, 'readonly' => false,
), ),
'd1' => array( 'd1' => array(
'dataType' => 'date', 'dataType' => 'date',
'actualType' => DataTypes::DATE,
'subType' => null,
'required' => true, 'required' => true,
'description' => '', 'description' => '',
'readonly' => false, 'readonly' => false,
), ),
'd2' => array( 'd2' => array(
'dataType' => 'date', 'dataType' => 'date',
'actualType' => DataTypes::DATE,
'subType' => null,
'required' => true, 'required' => true,
'description' => '', 'description' => '',
'readonly' => false, 'readonly' => false,
@ -199,6 +245,8 @@ class FormTypeParserTest extends \PHPUnit_Framework_TestCase
), ),
'c1' => array( 'c1' => array(
'dataType' => 'choice', 'dataType' => 'choice',
'actualType' => DataTypes::ENUM,
'subType' => null,
'required' => true, 'required' => true,
'description' => '', 'description' => '',
'readonly' => false, 'readonly' => false,
@ -206,6 +254,8 @@ class FormTypeParserTest extends \PHPUnit_Framework_TestCase
), ),
'c2' => array( 'c2' => array(
'dataType' => 'array of choices', 'dataType' => 'array of choices',
'actualType' => DataTypes::COLLECTION,
'subType' => DataTypes::ENUM,
'required' => true, 'required' => true,
'description' => '', 'description' => '',
'readonly' => false, 'readonly' => false,
@ -213,12 +263,16 @@ class FormTypeParserTest extends \PHPUnit_Framework_TestCase
), ),
'c3' => array( 'c3' => array(
'dataType' => 'choice', 'dataType' => 'choice',
'actualType' => DataTypes::ENUM,
'subType' => null,
'required' => true, 'required' => true,
'description' => '', 'description' => '',
'readonly' => false, 'readonly' => false,
), ),
'c4' => array( 'c4' => array(
'dataType' => 'choice', 'dataType' => 'choice',
'actualType' => DataTypes::ENUM,
'subType' => null,
'required' => true, 'required' => true,
'description' => '', 'description' => '',
'readonly' => false, 'readonly' => false,

View File

@ -1,6 +1,7 @@
<?php <?php
namespace NelmioApiDocBundle\Tests\Parser; namespace NelmioApiDocBundle\Tests\Parser;
use Nelmio\ApiDocBundle\DataTypes;
use Nelmio\ApiDocBundle\Tests\Fixtures\Model\JmsNested; use Nelmio\ApiDocBundle\Tests\Fixtures\Model\JmsNested;
use Nelmio\ApiDocBundle\Parser\JmsMetadataParser; use Nelmio\ApiDocBundle\Parser\JmsMetadataParser;
use JMS\Serializer\Metadata\ClassMetadata; use JMS\Serializer\Metadata\ClassMetadata;
@ -82,6 +83,8 @@ class JmsMetadataParserTest extends \PHPUnit_Framework_TestCase
array( array(
'foo' => array( 'foo' => array(
'dataType' => 'DateTime', 'dataType' => 'DateTime',
'actualType' => DataTypes::DATETIME,
'subType' => null,
'required' => false, 'required' => false,
'description' => null, 'description' => null,
'readonly' => false, 'readonly' => false,
@ -90,6 +93,8 @@ class JmsMetadataParserTest extends \PHPUnit_Framework_TestCase
), ),
'bar' => array( 'bar' => array(
'dataType' => 'string', 'dataType' => 'string',
'actualType' => DataTypes::STRING,
'subType' => null,
'required' => false, 'required' => false,
'description' => null, 'description' => null,
'readonly' => false, 'readonly' => false,
@ -98,6 +103,8 @@ class JmsMetadataParserTest extends \PHPUnit_Framework_TestCase
), ),
'baz' => array( 'baz' => array(
'dataType' => 'array of integers', 'dataType' => 'array of integers',
'actualType' => DataTypes::COLLECTION,
'subType' => DataTypes::INTEGER,
'required' => false, 'required' => false,
'description' => null, 'description' => null,
'readonly' => false, 'readonly' => false,
@ -155,6 +162,8 @@ class JmsMetadataParserTest extends \PHPUnit_Framework_TestCase
array( array(
'foo' => array( 'foo' => array(
'dataType' => 'string', 'dataType' => 'string',
'actualType' => DataTypes::STRING,
'subType' => null,
'required' => false, 'required' => false,
'description' => null, 'description' => null,
'readonly' => false, 'readonly' => false,
@ -163,6 +172,8 @@ class JmsMetadataParserTest extends \PHPUnit_Framework_TestCase
), ),
'bar' => array( 'bar' => array(
'dataType' => 'string', 'dataType' => 'string',
'actualType' => DataTypes::STRING,
'subType' => null,
'required' => false, 'required' => false,
'description' => null, 'description' => null,
'readonly' => false, 'readonly' => false,
@ -171,6 +182,8 @@ class JmsMetadataParserTest extends \PHPUnit_Framework_TestCase
), ),
'baz' => array( 'baz' => array(
'dataType' => 'string', 'dataType' => 'string',
'actualType' => DataTypes::STRING,
'subType' => null,
'required' => false, 'required' => false,
'description' => null, 'description' => null,
'readonly' => false, 'readonly' => false,
@ -193,6 +206,8 @@ class JmsMetadataParserTest extends \PHPUnit_Framework_TestCase
array( array(
'foo' => array( 'foo' => array(
'dataType' => 'string', 'dataType' => 'string',
'actualType' => DataTypes::STRING,
'subType' => null,
'required' => false, 'required' => false,
'description' => null, 'description' => null,
'readonly' => false, 'readonly' => false,
@ -201,6 +216,8 @@ class JmsMetadataParserTest extends \PHPUnit_Framework_TestCase
), ),
'bar' => array( 'bar' => array(
'dataType' => 'string', 'dataType' => 'string',
'actualType' => DataTypes::STRING,
'subType' => null,
'required' => false, 'required' => false,
'description' => null, 'description' => null,
'readonly' => false, 'readonly' => false,
@ -223,6 +240,8 @@ class JmsMetadataParserTest extends \PHPUnit_Framework_TestCase
array( array(
'bar' => array( 'bar' => array(
'dataType' => 'string', 'dataType' => 'string',
'actualType' => DataTypes::STRING,
'subType' => null,
'required' => false, 'required' => false,
'description' => null, 'description' => null,
'readonly' => false, 'readonly' => false,
@ -231,6 +250,8 @@ class JmsMetadataParserTest extends \PHPUnit_Framework_TestCase
), ),
'baz' => array( 'baz' => array(
'dataType' => 'string', 'dataType' => 'string',
'actualType' => DataTypes::STRING,
'subType' => null,
'required' => false, 'required' => false,
'description' => null, 'description' => null,
'readonly' => false, 'readonly' => false,
@ -253,6 +274,8 @@ class JmsMetadataParserTest extends \PHPUnit_Framework_TestCase
array( array(
'foo' => array( 'foo' => array(
'dataType' => 'string', 'dataType' => 'string',
'actualType' => DataTypes::STRING,
'subType' => null,
'required' => false, 'required' => false,
'description' => null, 'description' => null,
'readonly' => false, 'readonly' => false,
@ -261,6 +284,8 @@ class JmsMetadataParserTest extends \PHPUnit_Framework_TestCase
), ),
'bar' => array( 'bar' => array(
'dataType' => 'string', 'dataType' => 'string',
'actualType' => DataTypes::STRING,
'subType' => null,
'required' => false, 'required' => false,
'description' => null, 'description' => null,
'readonly' => false, 'readonly' => false,
@ -269,6 +294,8 @@ class JmsMetadataParserTest extends \PHPUnit_Framework_TestCase
), ),
'baz' => array( 'baz' => array(
'dataType' => 'string', 'dataType' => 'string',
'actualType' => DataTypes::STRING,
'subType' => null,
'required' => false, 'required' => false,
'description' => null, 'description' => null,
'readonly' => false, 'readonly' => false,
@ -325,26 +352,32 @@ class JmsMetadataParserTest extends \PHPUnit_Framework_TestCase
$this->assertEquals( $this->assertEquals(
array( array(
'foo' => array( 'foo' => array(
'dataType' => 'string', 'dataType' => 'string',
'required' => false, 'actualType' => DataTypes::STRING,
'description' => null, 'subType' => null,
'readonly' => false, 'required' => false,
'description' => null,
'readonly' => false,
'sinceVersion' => null, 'sinceVersion' => null,
'untilVersion' => null, 'untilVersion' => null,
), ),
'bar' => array( 'bar' => array(
'dataType' => 'string', 'dataType' => 'string',
'required' => false, 'actualType' => DataTypes::STRING,
'description' => null, 'subType' => null,
'readonly' => false, 'required' => false,
'description' => null,
'readonly' => false,
'sinceVersion' => '2.0', 'sinceVersion' => '2.0',
'untilVersion' => null, 'untilVersion' => null,
), ),
'baz' => array( 'baz' => array(
'dataType' => 'string', 'dataType' => 'string',
'required' => false, 'actualType' => DataTypes::STRING,
'description' => null, 'subType' => null,
'readonly' => false, 'required' => false,
'description' => null,
'readonly' => false,
'sinceVersion' => null, 'sinceVersion' => null,
'untilVersion' => '3.0', 'untilVersion' => '3.0',
) )

View File

@ -1,6 +1,7 @@
<?php <?php
namespace NelmioApiDocBundle\Tests\Parser; namespace NelmioApiDocBundle\Tests\Parser;
use Nelmio\ApiDocBundle\DataTypes;
use Nelmio\ApiDocBundle\Tests\WebTestCase; use Nelmio\ApiDocBundle\Tests\WebTestCase;
use Nelmio\ApiDocBundle\Parser\ValidationParser; use Nelmio\ApiDocBundle\Parser\ValidationParser;
use Nelmio\ApiDocBundle\Parser\ValidationParserLegacy; use Nelmio\ApiDocBundle\Parser\ValidationParserLegacy;
@ -28,7 +29,6 @@ class ValidationParserTest extends WebTestCase
public function testParser($property, $expected) public function testParser($property, $expected)
{ {
$result = $this->parser->parse(array('class' => 'Nelmio\ApiDocBundle\Tests\Fixtures\Model\ValidatorTest')); $result = $this->parser->parse(array('class' => 'Nelmio\ApiDocBundle\Tests\Fixtures\Model\ValidatorTest'));
foreach ($expected as $name => $value) { foreach ($expected as $name => $value) {
$this->assertArrayHasKey($property, $result); $this->assertArrayHasKey($property, $result);
$this->assertArrayHasKey($name, $result[$property]); $this->assertArrayHasKey($name, $result[$property]);
@ -66,25 +66,29 @@ class ValidationParserTest extends WebTestCase
array( array(
'property' => 'type', 'property' => 'type',
'expected' => array( 'expected' => array(
'dataType' => 'DateTime' 'dataType' => 'DateTime',
'actualType' => DataTypes::DATETIME,
) )
), ),
array( array(
'property' => 'date', 'property' => 'date',
'expected' => array( 'expected' => array(
'format' => '{Date YYYY-MM-DD}' 'format' => '{Date YYYY-MM-DD}',
'actualType' => DataTypes::DATE,
) )
), ),
array( array(
'property' => 'dateTime', 'property' => 'dateTime',
'expected' => array( 'expected' => array(
'format' => '{DateTime YYYY-MM-DD HH:MM:SS}' 'format' => '{DateTime YYYY-MM-DD HH:MM:SS}',
'actualType' => DataTypes::DATETIME,
) )
), ),
array( array(
'property' => 'time', 'property' => 'time',
'expected' => array( 'expected' => array(
'format' => '{Time HH:MM:SS}' 'format' => '{Time HH:MM:SS}',
'actualType' => DataTypes::TIME,
) )
), ),
array( array(
@ -108,19 +112,24 @@ class ValidationParserTest extends WebTestCase
array( array(
'property' => 'singlechoice', 'property' => 'singlechoice',
'expected' => array( 'expected' => array(
'format' => '[a|b]' 'format' => '[a|b]',
'actualType' => DataTypes::ENUM,
) )
), ),
array( array(
'property' => 'multiplechoice', 'property' => 'multiplechoice',
'expected' => array( 'expected' => array(
'format' => '{choice of [x|y|z]}' 'format' => '{choice of [x|y|z]}',
'actualType' => DataTypes::COLLECTION,
'subType' => DataTypes::ENUM,
) )
), ),
array( array(
'property' => 'multiplerangechoice', 'property' => 'multiplerangechoice',
'expected' => array( 'expected' => array(
'format' => '{min: 2 max: 3 choice of [foo|bar|baz|qux]}' 'format' => '{min: 2 max: 3 choice of [foo|bar|baz|qux]}',
'actualType' => DataTypes::COLLECTION,
'subType' => DataTypes::ENUM,
) )
), ),
array( array(