mirror of
https://github.com/retailcrm/NelmioApiDocBundle.git
synced 2025-03-10 09:36:10 +03:00
Added the initial structure for a Symfony Validation handler that is injected into the parsers.
This commit is contained in:
parent
6ba548e21e
commit
0913157399
@ -73,6 +73,7 @@ abstract class AbstractFormatter implements FormatterInterface
|
|||||||
'dataType' => $info['dataType'],
|
'dataType' => $info['dataType'],
|
||||||
'readonly' => $info['readonly'],
|
'readonly' => $info['readonly'],
|
||||||
'required' => $info['required'],
|
'required' => $info['required'],
|
||||||
|
'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
|
||||||
{
|
{
|
||||||
|
@ -23,6 +23,11 @@ class FormTypeParser implements ParserInterface
|
|||||||
*/
|
*/
|
||||||
protected $formFactory;
|
protected $formFactory;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var \Symfony\Component\Form\FormRegistry
|
||||||
|
*/
|
||||||
|
protected $formRegistry;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @var array
|
* @var array
|
||||||
*/
|
*/
|
||||||
@ -82,6 +87,7 @@ class FormTypeParser implements ParserInterface
|
|||||||
|
|
||||||
private function parseForm($form, $prefix = null)
|
private function parseForm($form, $prefix = null)
|
||||||
{
|
{
|
||||||
|
$className = get_class($form);
|
||||||
$parameters = array();
|
$parameters = array();
|
||||||
foreach ($form as $name => $child) {
|
foreach ($form as $name => $child) {
|
||||||
$config = $child->getConfig();
|
$config = $child->getConfig();
|
||||||
|
115
Parser/SymfonyValidationParser.php
Normal file
115
Parser/SymfonyValidationParser.php
Normal file
@ -0,0 +1,115 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Nelmio\ApiDocBundle\Parser;
|
||||||
|
|
||||||
|
use Nelmio\ApiDocBundle\Parser\ParserInterface;
|
||||||
|
use Symfony\Component\Validator\MetadataFactoryInterface;
|
||||||
|
use Symfony\Component\Validator\Constraint;
|
||||||
|
|
||||||
|
class SymfonyValidationParser implements ParserInterface
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @var \Symfony\Component\Validator\MetadataFactoryInterface
|
||||||
|
*/
|
||||||
|
protected $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)
|
||||||
|
{
|
||||||
|
$vparams = array();
|
||||||
|
$className = $input['class'];
|
||||||
|
|
||||||
|
$classdata = $this->factory->getMetadataFor($className);
|
||||||
|
|
||||||
|
if($classdata->hasPropertyMetadata($name)) {
|
||||||
|
$propdata = $classdata->getPropertyMetadata($name);
|
||||||
|
$propdata = reset($propdata);
|
||||||
|
$constraints = $propdata->getConstraints();
|
||||||
|
|
||||||
|
foreach($constraints as $constraint) {
|
||||||
|
$vparams = $this->parseConstraint($constraint, $vparams);
|
||||||
|
}
|
||||||
|
|
||||||
|
if(isset($vparams['format'])) {
|
||||||
|
$vparams['format'] = join(', ', $vparams['format']);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return $vparams;
|
||||||
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
@ -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.symfony_validation_parser.class">Nelmio\ApiDocBundle\Parser\SymfonyValidationParser</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>
|
||||||
@ -18,6 +19,10 @@
|
|||||||
<argument type="service" id="form.registry" />
|
<argument type="service" id="form.registry" />
|
||||||
<tag name="nelmio_api_doc.extractor.parser" />
|
<tag name="nelmio_api_doc.extractor.parser" />
|
||||||
</service>
|
</service>
|
||||||
|
<service id="nelmio_api_doc.parser.symfony_validation_parser" class="%nelmio_api_doc.parser.symfony_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;
|
||||||
|
}
|
128
Tests/Parser/SymfonyValidationHandlerTest.php
Normal file
128
Tests/Parser/SymfonyValidationHandlerTest.php
Normal file
@ -0,0 +1,128 @@
|
|||||||
|
<?php
|
||||||
|
namespace NelmioApiDocBundle\Tests\Parser;
|
||||||
|
|
||||||
|
use Nelmio\ApiDocBundle\Tests\WebTestCase;
|
||||||
|
use Nelmio\ApiDocBundle\Parser\Handler\SymfonyValidationHandler;
|
||||||
|
|
||||||
|
|
||||||
|
class SymfonyValidationHandlerTest extends WebTestCase
|
||||||
|
{
|
||||||
|
protected $handler;
|
||||||
|
|
||||||
|
public function setUp()
|
||||||
|
{
|
||||||
|
$container = $this->getContainer();
|
||||||
|
$factory = $container->get('validator.mapping.class_metadata_factory');
|
||||||
|
|
||||||
|
$this->handler = new SymfonyValidationHandler($factory);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @dataProvider dataTestHandler
|
||||||
|
*/
|
||||||
|
public function testHandler($property, $expected)
|
||||||
|
{
|
||||||
|
$result = $this->handler->handle('Nelmio\ApiDocBundle\Tests\Fixtures\Model\ValidatorTest', $property, array());
|
||||||
|
|
||||||
|
$this->assertEquals($expected, $result);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public function dataTestHandler()
|
||||||
|
{
|
||||||
|
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