mirror of
https://github.com/retailcrm/NelmioApiDocBundle.git
synced 2025-02-02 15:51:48 +03:00
Merge pull request #210 from jhallbachner/validation2
Added Support for Validation Component (refactored)
This commit is contained in:
commit
96b40b8a8c
@ -14,6 +14,7 @@ namespace Nelmio\ApiDocBundle\Extractor;
|
|||||||
use Doctrine\Common\Annotations\Reader;
|
use Doctrine\Common\Annotations\Reader;
|
||||||
use Nelmio\ApiDocBundle\Annotation\ApiDoc;
|
use Nelmio\ApiDocBundle\Annotation\ApiDoc;
|
||||||
use Nelmio\ApiDocBundle\Parser\ParserInterface;
|
use Nelmio\ApiDocBundle\Parser\ParserInterface;
|
||||||
|
use Nelmio\ApiDocBundle\Parser\PostParserInterface;
|
||||||
use Symfony\Component\Routing\Route;
|
use Symfony\Component\Routing\Route;
|
||||||
use Symfony\Component\Routing\RouterInterface;
|
use Symfony\Component\Routing\RouterInterface;
|
||||||
use Symfony\Component\DependencyInjection\ContainerInterface;
|
use Symfony\Component\DependencyInjection\ContainerInterface;
|
||||||
@ -268,13 +269,24 @@ class ApiDocExtractor
|
|||||||
|
|
||||||
$normalizedInput = $this->normalizeClassParameter($input);
|
$normalizedInput = $this->normalizeClassParameter($input);
|
||||||
|
|
||||||
|
$supportedParsers = array();
|
||||||
|
$parameters = array();
|
||||||
foreach ($this->parsers as $parser) {
|
foreach ($this->parsers as $parser) {
|
||||||
if ($parser->supports($normalizedInput)) {
|
if ($parser->supports($normalizedInput)) {
|
||||||
$parameters = $parser->parse($normalizedInput);
|
$supportedParsers[] = $parser;
|
||||||
break;
|
$parameters = $this->mergeParameters($parameters, $parser->parse($normalizedInput));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
foreach($supportedParsers as $parser) {
|
||||||
|
if($parser instanceof PostParserInterface) {
|
||||||
|
$mp = $parser->postParse($normalizedInput, $parameters);
|
||||||
|
$parameters = $this->mergeParameters($parameters, $mp);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$parameters = $this->clearClasses($parameters);
|
||||||
|
|
||||||
if ('PUT' === $method) {
|
if ('PUT' === $method) {
|
||||||
// All parameters are optional with PUT (update)
|
// All parameters are optional with PUT (update)
|
||||||
array_walk($parameters, function($val, $key) use (&$data) {
|
array_walk($parameters, function($val, $key) use (&$data) {
|
||||||
@ -294,9 +306,9 @@ class ApiDocExtractor
|
|||||||
foreach ($this->parsers as $parser) {
|
foreach ($this->parsers as $parser) {
|
||||||
if ($parser->supports($normalizedOutput)) {
|
if ($parser->supports($normalizedOutput)) {
|
||||||
$response = $parser->parse($normalizedOutput);
|
$response = $parser->parse($normalizedOutput);
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
$response = $this->clearClasses($response);
|
||||||
|
|
||||||
$annotation->setResponse($response);
|
$annotation->setResponse($response);
|
||||||
}
|
}
|
||||||
@ -374,6 +386,57 @@ class ApiDocExtractor
|
|||||||
return array_merge($defaults, $input);
|
return array_merge($defaults, $input);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Merges two parameter arrays together. This logic allows documentation to be built
|
||||||
|
* from multiple parser passes, with later passes extending previous passes:
|
||||||
|
* - Boolean values are true if any parser returns true.
|
||||||
|
* - Requirement parameters are concatenated.
|
||||||
|
* - Other string values are overridden by later parsers when present.
|
||||||
|
* - Array parameters are recursively merged.
|
||||||
|
*
|
||||||
|
* @param array $p1 The pre-existing parameters array.
|
||||||
|
* @param array $p2 The newly-returned parameters array.
|
||||||
|
* @return array The resulting, merged array.
|
||||||
|
*/
|
||||||
|
protected function mergeParameters($p1, $p2)
|
||||||
|
{
|
||||||
|
$params = $p1;
|
||||||
|
|
||||||
|
foreach($p2 as $propname => $propvalue) {
|
||||||
|
if(!isset($p1[$propname])) {
|
||||||
|
$params[$propname] = $propvalue;
|
||||||
|
} else {
|
||||||
|
$v1 = $p1[$propname];
|
||||||
|
|
||||||
|
foreach($propvalue as $name => $value) {
|
||||||
|
if(is_array($value)) {
|
||||||
|
if(isset($v1[$name]) && is_array($v1[$name])) {
|
||||||
|
$v1[$name] = $this->mergeParameters($v1[$name], $value);
|
||||||
|
} else {
|
||||||
|
$v1[$name] = $value;
|
||||||
|
}
|
||||||
|
} elseif(!is_null($value)) {
|
||||||
|
if(in_array($name, array('required', 'readonly'))) {
|
||||||
|
$v1[$name] = $v1[$name] || $value;
|
||||||
|
} elseif(in_array($name, array('requirement'))) {
|
||||||
|
if(isset($v1[$name])) {
|
||||||
|
$v1[$name] .= ', ' . $value;
|
||||||
|
} else {
|
||||||
|
$v1[$name] = $value;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
$v1[$name] = $value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$params[$propname] = $v1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return $params;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Parses annotations for a given method, and adds new information to the given ApiDoc
|
* Parses annotations for a given method, and adds new information to the given ApiDoc
|
||||||
* annotation. Useful to extract information from the FOSRestBundle annotations.
|
* annotation. Useful to extract information from the FOSRestBundle annotations.
|
||||||
@ -389,4 +452,21 @@ class ApiDocExtractor
|
|||||||
$handler->handle($annotation, $annots, $route, $method);
|
$handler->handle($annotation, $annots, $route, $method);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Clears the temporary 'class' parameter from the parameters array before it is returned.
|
||||||
|
*
|
||||||
|
* @param array $array The source array.
|
||||||
|
* @return array The cleared array.
|
||||||
|
*/
|
||||||
|
protected function clearClasses($array)
|
||||||
|
{
|
||||||
|
if(is_array($array)) {
|
||||||
|
unset($array['class']);
|
||||||
|
foreach($array as $name => $item) {
|
||||||
|
$array[$name] = $this->clearClasses($item);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return $array;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -69,10 +69,11 @@ abstract class AbstractFormatter implements FormatterInterface
|
|||||||
$newName = $this->getNewName($name, $info, $parentName);
|
$newName = $this->getNewName($name, $info, $parentName);
|
||||||
|
|
||||||
$newParams[$newName] = array(
|
$newParams[$newName] = array(
|
||||||
'description' => $info['description'],
|
|
||||||
'dataType' => $info['dataType'],
|
'dataType' => $info['dataType'],
|
||||||
'readonly' => $info['readonly'],
|
'readonly' => $info['readonly'],
|
||||||
'required' => $info['required'],
|
'required' => $info['required'],
|
||||||
|
'description' => array_key_exists('description', $info) ? $info['description'] : 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,
|
||||||
);
|
);
|
||||||
|
@ -7,6 +7,7 @@ use Symfony\Component\DependencyInjection\ContainerBuilder;
|
|||||||
use Nelmio\ApiDocBundle\DependencyInjection\RegisterJmsParserPass;
|
use Nelmio\ApiDocBundle\DependencyInjection\RegisterJmsParserPass;
|
||||||
use Nelmio\ApiDocBundle\DependencyInjection\RegisterExtractorParsersPass;
|
use Nelmio\ApiDocBundle\DependencyInjection\RegisterExtractorParsersPass;
|
||||||
use Nelmio\ApiDocBundle\DependencyInjection\ExtractorHandlerCompilerPass;
|
use Nelmio\ApiDocBundle\DependencyInjection\ExtractorHandlerCompilerPass;
|
||||||
|
use Nelmio\ApiDocBundle\DependencyInjection\ParserHandlerCompilerPass;
|
||||||
|
|
||||||
class NelmioApiDocBundle extends Bundle
|
class NelmioApiDocBundle extends Bundle
|
||||||
{
|
{
|
||||||
|
@ -24,6 +24,11 @@ class FormTypeParser implements ParserInterface
|
|||||||
*/
|
*/
|
||||||
protected $formFactory;
|
protected $formFactory;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var \Symfony\Component\Form\FormRegistry
|
||||||
|
*/
|
||||||
|
protected $formRegistry;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @var array
|
* @var array
|
||||||
*/
|
*/
|
||||||
@ -129,6 +134,8 @@ class FormTypeParser implements ParserInterface
|
|||||||
'description' => $config->getAttribute('description'),
|
'description' => $config->getAttribute('description'),
|
||||||
'readonly' => $config->getDisabled(),
|
'readonly' => $config->getDisabled(),
|
||||||
);
|
);
|
||||||
|
} else {
|
||||||
|
$parameters[$name]['class'] = $type;
|
||||||
}
|
}
|
||||||
|
|
||||||
continue;
|
continue;
|
||||||
|
@ -126,6 +126,10 @@ class JmsMetadataParser implements ParserInterface
|
|||||||
'untilVersion' => $item->untilVersion,
|
'untilVersion' => $item->untilVersion,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
if(!is_null($dataType['class'])) {
|
||||||
|
$params[$name]['class'] = $dataType['class'];
|
||||||
|
}
|
||||||
|
|
||||||
// if class already parsed, continue, to avoid infinite recursion
|
// if class already parsed, continue, to avoid infinite recursion
|
||||||
if (in_array($dataType['class'], $visited)) {
|
if (in_array($dataType['class'], $visited)) {
|
||||||
continue;
|
continue;
|
||||||
|
@ -33,6 +33,8 @@ interface ParserInterface
|
|||||||
* - readonly boolean
|
* - readonly boolean
|
||||||
* - children (optional) array of nested property names mapped to arrays
|
* - children (optional) array of nested property names mapped to arrays
|
||||||
* in the format described here
|
* in the format described here
|
||||||
|
* - class (optional) the fully-qualified class name of the item, if
|
||||||
|
* it is represented by an object
|
||||||
*
|
*
|
||||||
* @param string $item The string type of input to parse.
|
* @param string $item The string type of input to parse.
|
||||||
* @return array
|
* @return array
|
||||||
|
38
Parser/PostParserInterface.php
Normal file
38
Parser/PostParserInterface.php
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
<?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\Parser;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This is the interface parsers must implement in order to register a second parsing pass after the initial structure
|
||||||
|
* is populated..
|
||||||
|
*/
|
||||||
|
interface PostParserInterface
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Reparses an object for additional documentation details after it has already been parsed once, to allow
|
||||||
|
* parsers to extend information initially documented by other parsers.
|
||||||
|
*
|
||||||
|
* Returns an array of class property metadata where each item is a key (the property name) and
|
||||||
|
* an array of data with the following keys:
|
||||||
|
* - dataType string
|
||||||
|
* - required boolean
|
||||||
|
* - description string
|
||||||
|
* - readonly boolean
|
||||||
|
* - children (optional) array of nested property names mapped to arrays
|
||||||
|
* in the format described here
|
||||||
|
*
|
||||||
|
* @param 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);
|
||||||
|
}
|
178
Parser/ValidationParser.php
Normal file
178
Parser/ValidationParser.php
Normal file
@ -0,0 +1,178 @@
|
|||||||
|
<?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\Parser;
|
||||||
|
|
||||||
|
use Symfony\Component\Validator\MetadataFactoryInterface;
|
||||||
|
use Symfony\Component\Validator\Constraint;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Uses the Symfony Validation component to extract information about API objects.
|
||||||
|
*/
|
||||||
|
class ValidationParser implements ParserInterface, PostParserInterface
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @var \Symfony\Component\Validator\MetadataFactoryInterface
|
||||||
|
*/
|
||||||
|
protected $factory;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Requires a validation MetadataFactory.
|
||||||
|
*
|
||||||
|
* @param MetadataFactoryInterface $factory
|
||||||
|
*/
|
||||||
|
public function __construct(MetadataFactoryInterface $factory)
|
||||||
|
{
|
||||||
|
$this->factory = $factory;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
public function supports(array $input)
|
||||||
|
{
|
||||||
|
$className = $input['class'];
|
||||||
|
|
||||||
|
return $this->factory->hasMetadataFor($className);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
public function parse(array $input)
|
||||||
|
{
|
||||||
|
$params = array();
|
||||||
|
$className = $input['class'];
|
||||||
|
|
||||||
|
$classdata = $this->factory->getMetadataFor($className);
|
||||||
|
$properties = $classdata->getConstrainedProperties();
|
||||||
|
|
||||||
|
foreach($properties as $property) {
|
||||||
|
$vparams = array();
|
||||||
|
$pds = $classdata->getPropertyMetadata($property);
|
||||||
|
foreach($pds as $propdata) {
|
||||||
|
$constraints = $propdata->getConstraints();
|
||||||
|
|
||||||
|
foreach($constraints as $constraint) {
|
||||||
|
$vparams = $this->parseConstraint($constraint, $vparams);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if(isset($vparams['format'])) {
|
||||||
|
$vparams['format'] = join(', ', $vparams['format']);
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach(array('dataType', 'readonly', 'required') as $reqprop) {
|
||||||
|
if(!isset($vparams[$reqprop])) {
|
||||||
|
$vparams[$reqprop] = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$params[$property] = $vparams;
|
||||||
|
}
|
||||||
|
|
||||||
|
return $params;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritDoc}
|
||||||
|
*/
|
||||||
|
public function postParse(array $input, array $parameters)
|
||||||
|
{
|
||||||
|
foreach($parameters as $param => $data) {
|
||||||
|
if(isset($data['class']) && isset($data['children'])) {
|
||||||
|
$input = array('class' => $data['class']);
|
||||||
|
$parameters[$param]['children'] = array_merge(
|
||||||
|
$parameters[$param]['children'], $this->postParse($input, $parameters[$param]['children'])
|
||||||
|
);
|
||||||
|
$parameters[$param]['children'] = array_merge(
|
||||||
|
$parameters[$param]['children'], $this->parse($input, $parameters[$param]['children'])
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return $parameters;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a valid documentation parameter based on an individual validation Constraint.
|
||||||
|
* Currently supports:
|
||||||
|
* - NotBlank/NotNull
|
||||||
|
* - Type
|
||||||
|
* - Email
|
||||||
|
* - Url
|
||||||
|
* - Ip
|
||||||
|
* - Length (min and max)
|
||||||
|
* - Choice (single and multiple, min and max)
|
||||||
|
* - Regex (match and non-match)
|
||||||
|
*
|
||||||
|
* @param Constraint $constraint The constraint metadata object.
|
||||||
|
* @param array $vparams The existing validation parameters.
|
||||||
|
* @return mixed The parsed list of validation parameters.
|
||||||
|
*/
|
||||||
|
protected function parseConstraint(Constraint $constraint, $vparams)
|
||||||
|
{
|
||||||
|
$class = substr(get_class($constraint), strlen('Symfony\\Component\\Validator\\Constraints\\'));
|
||||||
|
|
||||||
|
switch($class) {
|
||||||
|
case 'NotBlank':
|
||||||
|
case 'NotNull':
|
||||||
|
$vparams['required'] = true;
|
||||||
|
break;
|
||||||
|
case 'Type':
|
||||||
|
$vparams['dataType'] = $constraint->type;
|
||||||
|
break;
|
||||||
|
case 'Email':
|
||||||
|
$vparams['format'][] = '{email address}';
|
||||||
|
break;
|
||||||
|
case 'Url':
|
||||||
|
$vparams['format'][] = '{url}';
|
||||||
|
break;
|
||||||
|
case 'Ip':
|
||||||
|
$vparams['format'][] = '{ip address}';
|
||||||
|
break;
|
||||||
|
case 'Length':
|
||||||
|
$messages = array();
|
||||||
|
if(isset($constraint->min)) {
|
||||||
|
$messages[] = "min: {$constraint->min}";
|
||||||
|
}
|
||||||
|
if(isset($constraint->max)) {
|
||||||
|
$messages[] = "max: {$constraint->max}";
|
||||||
|
}
|
||||||
|
$vparams['format'][] = '{length: ' . join(', ', $messages) . '}';
|
||||||
|
break;
|
||||||
|
case 'Choice':
|
||||||
|
$format = '[' . join('|', $constraint->choices) . ']';
|
||||||
|
if($constraint->multiple) {
|
||||||
|
$messages = array();
|
||||||
|
if(isset($constraint->min)) {
|
||||||
|
$messages[] = "min: {$constraint->min} ";
|
||||||
|
}
|
||||||
|
if(isset($constraint->max)) {
|
||||||
|
$messages[] = "max: {$constraint->max} ";
|
||||||
|
}
|
||||||
|
$vparams['format'][] = '{' . join ('', $messages) . 'choice of ' . $format . '}';
|
||||||
|
} else {
|
||||||
|
$vparams['format'][] = $format;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 'Regex':
|
||||||
|
if($constraint->match) {
|
||||||
|
$vparams['format'][] = '{match: ' . $constraint->pattern . '}';
|
||||||
|
} else {
|
||||||
|
$vparams['format'][] = '{not match: ' . $constraint->pattern . '}';
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return $vparams;
|
||||||
|
}
|
||||||
|
}
|
@ -104,8 +104,9 @@ The following properties are available:
|
|||||||
|
|
||||||
* `filters`: an array of filters;
|
* `filters`: an array of filters;
|
||||||
|
|
||||||
* `input`: the input type associated to the method, currently this supports Form Types, and classes with JMS Serializer
|
* `input`: the input type associated to the method (currently this supports Form Types, classes with JMS Serializer
|
||||||
metadata, useful for POST|PUT methods, either as FQCN or as form type (if it is registered in the form factory in the container).
|
metadata, and classes with Validation component metadata) useful for POST|PUT methods, either as FQCN or as form type
|
||||||
|
(if it is registered in the form factory in the container).
|
||||||
|
|
||||||
* `output`: the output type associated with the response. Specified and parsed the same way as `input`.
|
* `output`: the output type associated with the response. Specified and parsed the same way as `input`.
|
||||||
|
|
||||||
|
@ -5,6 +5,7 @@
|
|||||||
|
|
||||||
<parameters>
|
<parameters>
|
||||||
<parameter key="nelmio_api_doc.parser.form_type_parser.class">Nelmio\ApiDocBundle\Parser\FormTypeParser</parameter>
|
<parameter key="nelmio_api_doc.parser.form_type_parser.class">Nelmio\ApiDocBundle\Parser\FormTypeParser</parameter>
|
||||||
|
<parameter key="nelmio_api_doc.parser.validation_parser.class">Nelmio\ApiDocBundle\Parser\ValidationParser</parameter>
|
||||||
<parameter key="nelmio_api_doc.formatter.abstract_formatter.class">Nelmio\ApiDocBundle\Formatter\AbstractFormatter</parameter>
|
<parameter key="nelmio_api_doc.formatter.abstract_formatter.class">Nelmio\ApiDocBundle\Formatter\AbstractFormatter</parameter>
|
||||||
<parameter key="nelmio_api_doc.formatter.markdown_formatter.class">Nelmio\ApiDocBundle\Formatter\MarkdownFormatter</parameter>
|
<parameter key="nelmio_api_doc.formatter.markdown_formatter.class">Nelmio\ApiDocBundle\Formatter\MarkdownFormatter</parameter>
|
||||||
<parameter key="nelmio_api_doc.formatter.simple_formatter.class">Nelmio\ApiDocBundle\Formatter\SimpleFormatter</parameter>
|
<parameter key="nelmio_api_doc.formatter.simple_formatter.class">Nelmio\ApiDocBundle\Formatter\SimpleFormatter</parameter>
|
||||||
@ -17,6 +18,10 @@
|
|||||||
<argument type="service" id="form.factory" />
|
<argument type="service" id="form.factory" />
|
||||||
<tag name="nelmio_api_doc.extractor.parser" />
|
<tag name="nelmio_api_doc.extractor.parser" />
|
||||||
</service>
|
</service>
|
||||||
|
<service id="nelmio_api_doc.parser.validation_parser" class="%nelmio_api_doc.parser.validation_parser.class%">
|
||||||
|
<argument type="service" id="validator.mapping.class_metadata_factory"/>
|
||||||
|
<tag name="nelmio_api_doc.extractor.parser" />
|
||||||
|
</service>
|
||||||
<service id="nelmio_api_doc.formatter.abstract_formatter" class="%nelmio_api_doc.formatter.abstract_formatter.class%" />
|
<service id="nelmio_api_doc.formatter.abstract_formatter" class="%nelmio_api_doc.formatter.abstract_formatter.class%" />
|
||||||
<service id="nelmio_api_doc.formatter.markdown_formatter" class="%nelmio_api_doc.formatter.markdown_formatter.class%"
|
<service id="nelmio_api_doc.formatter.markdown_formatter" class="%nelmio_api_doc.formatter.markdown_formatter.class%"
|
||||||
parent="nelmio_api_doc.formatter.abstract_formatter" />
|
parent="nelmio_api_doc.formatter.abstract_formatter" />
|
||||||
|
@ -15,7 +15,7 @@
|
|||||||
</parameters>
|
</parameters>
|
||||||
|
|
||||||
<services>
|
<services>
|
||||||
<service id='nelmio_api_doc.doc_comment_extractor' class='%nelmio_api_doc.doc_comment_extractor.class%' />
|
<service id='nelmio_api_doc.doc_comment_extractor' class="%nelmio_api_doc.doc_comment_extractor.class%" />
|
||||||
|
|
||||||
<service id="nelmio_api_doc.extractor.api_doc_extractor" class="%nelmio_api_doc.extractor.api_doc_extractor.class%">
|
<service id="nelmio_api_doc.extractor.api_doc_extractor" class="%nelmio_api_doc.extractor.api_doc_extractor.class%">
|
||||||
<argument type="service" id="service_container"/>
|
<argument type="service" id="service_container"/>
|
||||||
@ -46,7 +46,6 @@
|
|||||||
<service id="nelmio_api_doc.extractor.handler.sensio_framework_extra" class="%nelmio_api_doc.extractor.handler.sensio_framework_extra.class%" public="false">
|
<service id="nelmio_api_doc.extractor.handler.sensio_framework_extra" class="%nelmio_api_doc.extractor.handler.sensio_framework_extra.class%" public="false">
|
||||||
<tag name="nelmio_api_doc.extractor.handler"/>
|
<tag name="nelmio_api_doc.extractor.handler"/>
|
||||||
</service>
|
</service>
|
||||||
|
|
||||||
</services>
|
</services>
|
||||||
|
|
||||||
</container>
|
</container>
|
||||||
|
@ -109,6 +109,7 @@
|
|||||||
<th>Parameter</th>
|
<th>Parameter</th>
|
||||||
<th>Type</th>
|
<th>Type</th>
|
||||||
<th>Required?</th>
|
<th>Required?</th>
|
||||||
|
<th>Format</th>
|
||||||
<th>Description</th>
|
<th>Description</th>
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
@ -119,6 +120,7 @@
|
|||||||
<td>{{ name }}</td>
|
<td>{{ name }}</td>
|
||||||
<td>{{ infos.dataType }}</td>
|
<td>{{ infos.dataType }}</td>
|
||||||
<td>{{ infos.required ? 'true' : 'false' }}</td>
|
<td>{{ infos.required ? 'true' : 'false' }}</td>
|
||||||
|
<td>{{ infos.format }}</td>
|
||||||
<td>{{ infos.description }}</td>
|
<td>{{ infos.description }}</td>
|
||||||
</tr>
|
</tr>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
@ -216,7 +218,7 @@
|
|||||||
<p class="tuple">
|
<p class="tuple">
|
||||||
<input type="text" class="key" value="{{ name }}" placeholder="Key" />
|
<input type="text" class="key" value="{{ name }}" placeholder="Key" />
|
||||||
<span>=</span>
|
<span>=</span>
|
||||||
<input type="text" class="value" placeholder="{% if infos.dataType %}[{{ infos.dataType }}] {% endif %}{% if infos.description %}{{ infos.description }}{% else %}Value{% endif %}" /> <span class="remove">-</span>
|
<input type="text" class="value" placeholder="{% if infos.dataType %}[{{ infos.dataType }}] {% endif %}{% if infos.format %}{{ infos.format }}{% endif %}{% if infos.description %}{{ infos.description }}{% else %}Value{% endif %}" /> <span class="remove">-</span>
|
||||||
</p>
|
</p>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
|
95
Tests/Fixtures/Model/ValidatorTest.php
Normal file
95
Tests/Fixtures/Model/ValidatorTest.php
Normal file
@ -0,0 +1,95 @@
|
|||||||
|
<?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\Tests\Fixtures\Model;
|
||||||
|
|
||||||
|
use Symfony\Component\Validator\Constraints as Assert;
|
||||||
|
|
||||||
|
class ValidatorTest
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @Assert\Length(min=10);
|
||||||
|
*/
|
||||||
|
public $length10;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @Assert\Length(min=1, max=10)
|
||||||
|
*/
|
||||||
|
public $length1to10;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @Assert\NotBlank()
|
||||||
|
*/
|
||||||
|
public $notblank;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @Assert\NotNull()
|
||||||
|
*/
|
||||||
|
public $notnull;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @Assert\Type("DateTime");
|
||||||
|
*/
|
||||||
|
public $type;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @Assert\Email()
|
||||||
|
*/
|
||||||
|
public $email;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @Assert\Url()
|
||||||
|
*/
|
||||||
|
public $url;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @Assert\Ip()
|
||||||
|
*/
|
||||||
|
public $ip;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @Assert\Choice(choices={"a", "b"})
|
||||||
|
*/
|
||||||
|
public $singlechoice;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @Assert\Choice(choices={"x", "y", "z"}, multiple=true)
|
||||||
|
*/
|
||||||
|
public $multiplechoice;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @Assert\Choice(choices={"foo", "bar", "baz", "qux"}, multiple=true, min=2, max=3)
|
||||||
|
*/
|
||||||
|
public $multiplerangechoice;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @Assert\Regex(pattern="/^\d{1,4}\w{1,4}$/")
|
||||||
|
*/
|
||||||
|
public $regexmatch;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @Assert\Regex(pattern="/\d/", match=false)
|
||||||
|
*/
|
||||||
|
public $regexnomatch;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @Assert\NotNull()
|
||||||
|
* @Assert\Type("string")
|
||||||
|
* @Assert\Email()
|
||||||
|
*/
|
||||||
|
public $multipleassertions;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @Assert\Url()
|
||||||
|
* @Assert\Length(min=10)
|
||||||
|
*/
|
||||||
|
public $multipleformats;
|
||||||
|
}
|
130
Tests/Parser/ValidationParserTest.php
Normal file
130
Tests/Parser/ValidationParserTest.php
Normal file
@ -0,0 +1,130 @@
|
|||||||
|
<?php
|
||||||
|
namespace NelmioApiDocBundle\Tests\Parser;
|
||||||
|
|
||||||
|
use Nelmio\ApiDocBundle\Tests\WebTestCase;
|
||||||
|
use Nelmio\ApiDocBundle\Parser\ValidationParser;
|
||||||
|
|
||||||
|
|
||||||
|
class ValidationParserTest extends WebTestCase
|
||||||
|
{
|
||||||
|
protected $handler;
|
||||||
|
|
||||||
|
public function setUp()
|
||||||
|
{
|
||||||
|
$container = $this->getContainer();
|
||||||
|
$factory = $container->get('validator.mapping.class_metadata_factory');
|
||||||
|
|
||||||
|
$this->parser = new ValidationParser($factory);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @dataProvider dataTestParser
|
||||||
|
*/
|
||||||
|
public function testParser($property, $expected)
|
||||||
|
{
|
||||||
|
$result = $this->parser->parse(array('class' => 'Nelmio\ApiDocBundle\Tests\Fixtures\Model\ValidatorTest'));
|
||||||
|
|
||||||
|
foreach($expected as $name => $value) {
|
||||||
|
$this->assertEquals($value, $expected[$name]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public function dataTestParser()
|
||||||
|
{
|
||||||
|
return array(
|
||||||
|
array(
|
||||||
|
'property' => 'length10',
|
||||||
|
'expected' => array(
|
||||||
|
'format' => '{length: min: 10}'
|
||||||
|
)
|
||||||
|
),
|
||||||
|
array(
|
||||||
|
'property' => 'length1to10',
|
||||||
|
'expected' => array(
|
||||||
|
'format' => '{length: min: 1, max: 10}'
|
||||||
|
)
|
||||||
|
),
|
||||||
|
array(
|
||||||
|
'property' => 'notblank',
|
||||||
|
'expected' => array(
|
||||||
|
'required' => true
|
||||||
|
)
|
||||||
|
),
|
||||||
|
array(
|
||||||
|
'property' => 'notnull',
|
||||||
|
'expected' => array(
|
||||||
|
'required' => true
|
||||||
|
)
|
||||||
|
),
|
||||||
|
array(
|
||||||
|
'property' => 'type',
|
||||||
|
'expected' => array(
|
||||||
|
'dataType' => 'DateTime'
|
||||||
|
)
|
||||||
|
),
|
||||||
|
array(
|
||||||
|
'property' => 'email',
|
||||||
|
'expected' => array(
|
||||||
|
'format' => '{email address}'
|
||||||
|
)
|
||||||
|
),
|
||||||
|
array(
|
||||||
|
'property' => 'url',
|
||||||
|
'expected' => array(
|
||||||
|
'format' => '{url}'
|
||||||
|
)
|
||||||
|
),
|
||||||
|
array(
|
||||||
|
'property' => 'ip',
|
||||||
|
'expected' => array(
|
||||||
|
'format' => '{ip address}'
|
||||||
|
)
|
||||||
|
),
|
||||||
|
array(
|
||||||
|
'property' => 'singlechoice',
|
||||||
|
'expected' => array(
|
||||||
|
'format' => '[a|b]'
|
||||||
|
)
|
||||||
|
),
|
||||||
|
array(
|
||||||
|
'property' => 'multiplechoice',
|
||||||
|
'expected' => array(
|
||||||
|
'format' => '{choice of [x|y|z]}'
|
||||||
|
)
|
||||||
|
),
|
||||||
|
array(
|
||||||
|
'property' => 'multiplerangechoice',
|
||||||
|
'expected' => array(
|
||||||
|
'format' => '{min: 2 max: 3 choice of [foo|bar|baz|qux]}'
|
||||||
|
)
|
||||||
|
),
|
||||||
|
array(
|
||||||
|
'property' => 'regexmatch',
|
||||||
|
'expected' => array(
|
||||||
|
'format' => '{match: /^\d{1,4}\w{1,4}$/}'
|
||||||
|
)
|
||||||
|
),
|
||||||
|
array(
|
||||||
|
'property' => 'regexnomatch',
|
||||||
|
'expected' => array(
|
||||||
|
'format' => '{not match: /\d/}'
|
||||||
|
)
|
||||||
|
),
|
||||||
|
array(
|
||||||
|
'property' => 'multipleassertions',
|
||||||
|
'expected' => array(
|
||||||
|
'required' => true,
|
||||||
|
'dataType' => 'string',
|
||||||
|
'format' => '{email address}'
|
||||||
|
)
|
||||||
|
),
|
||||||
|
array(
|
||||||
|
'property' => 'multipleformats',
|
||||||
|
'expected' => array(
|
||||||
|
'format' => '{url}, {length: min: 10}'
|
||||||
|
)
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user