From 0baa67751588fe25c775f239b6a2b2f41ded7faf Mon Sep 17 00:00:00 2001 From: Vladislav Kopaygorodsky Date: Wed, 20 Sep 2017 08:18:58 +0300 Subject: [PATCH 1/5] Reading form recursively down(nested form) (#1087) * Added support for EntityType in FormModel Describer. Reading form recursively down. Modified formSupport test * codestyle fixes --- Describer/ModelRegistryAwareTrait.php | 3 ++ ModelDescriber/FormModelDescriber.php | 48 +++++++++++++++++-- Tests/Functional/Controller/ApiController.php | 22 +++++++++ Tests/Functional/Form/UserType.php | 32 +++++++++++++ Tests/Functional/FunctionalTest.php | 8 ++++ 5 files changed, 110 insertions(+), 3 deletions(-) create mode 100644 Tests/Functional/Form/UserType.php diff --git a/Describer/ModelRegistryAwareTrait.php b/Describer/ModelRegistryAwareTrait.php index da1ab36..3bcdeca 100644 --- a/Describer/ModelRegistryAwareTrait.php +++ b/Describer/ModelRegistryAwareTrait.php @@ -15,6 +15,9 @@ use Nelmio\ApiDocBundle\Model\ModelRegistry; trait ModelRegistryAwareTrait { + /** + * @var ModelRegistry + */ private $modelRegistry; public function setModelRegistry(ModelRegistry $modelRegistry) diff --git a/ModelDescriber/FormModelDescriber.php b/ModelDescriber/FormModelDescriber.php index 9a50111..07f5bcc 100644 --- a/ModelDescriber/FormModelDescriber.php +++ b/ModelDescriber/FormModelDescriber.php @@ -12,15 +12,22 @@ namespace Nelmio\ApiDocBundle\ModelDescriber; use EXSyst\Component\Swagger\Schema; +use Nelmio\ApiDocBundle\Describer\ModelRegistryAwareInterface; +use Nelmio\ApiDocBundle\Describer\ModelRegistryAwareTrait; use Nelmio\ApiDocBundle\Model\Model; +use Symfony\Component\Form\AbstractType; use Symfony\Component\Form\FormFactoryInterface; +use Symfony\Component\Form\FormInterface; use Symfony\Component\Form\FormTypeInterface; +use Symfony\Component\PropertyInfo\Type; /** * @internal */ -final class FormModelDescriber implements ModelDescriberInterface +final class FormModelDescriber implements ModelDescriberInterface, ModelRegistryAwareInterface { + use ModelRegistryAwareTrait; + private $formFactory; public function __construct(FormFactoryInterface $formFactory = null) @@ -30,7 +37,7 @@ final class FormModelDescriber implements ModelDescriberInterface public function describe(Model $model, Schema $schema) { - if (method_exists('Symfony\Component\Form\AbstractType', 'setDefaultOptions')) { + if (method_exists(AbstractType::class, 'setDefaultOptions')) { throw new \LogicException('symfony/form < 3.0 is not supported, please upgrade to an higher version to use a form as a model.'); } if (null === $this->formFactory) { @@ -51,9 +58,10 @@ final class FormModelDescriber implements ModelDescriberInterface return is_a($model->getType()->getClassName(), FormTypeInterface::class, true); } - private function parseForm(Schema $schema, $form) + private function parseForm(Schema $schema, FormInterface $form) { $properties = $schema->getProperties(); + foreach ($form as $name => $child) { $config = $child->getConfig(); $property = $properties->get($name); @@ -64,16 +72,24 @@ final class FormModelDescriber implements ModelDescriberInterface $property->setType('string'); break; } + + if ('number' === $blockPrefix) { + $property->setType('number'); + break; + } + if ('date' === $blockPrefix) { $property->setType('string'); $property->setFormat('date'); break; } + if ('datetime' === $blockPrefix) { $property->setType('string'); $property->setFormat('date-time'); break; } + if ('choice' === $blockPrefix) { $property->setType('string'); if (($choices = $config->getOption('choices')) && is_array($choices) && count($choices)) { @@ -85,6 +101,27 @@ final class FormModelDescriber implements ModelDescriberInterface if ('collection' === $blockPrefix) { $subType = $config->getOption('entry_type'); } + + if ('entity' === $blockPrefix) { + $entityClass = $config->getOption('class'); + + if ($config->getOption('multiple')) { + $property->setFormat(sprintf('[%s id]', $entityClass)); + $property->setType('array'); + $property->setExample('[1, 2, 3]'); + } else { + $property->setType('string'); + $property->setFormat(sprintf('%s id', $entityClass)); + } + break; + } + + if ($type->getInnerType() && ($formClass = get_class($type->getInnerType())) && !$this->isBuiltinType($formClass)) { + //if form type is not builtin in Form component. + $model = new Model(new Type(Type::BUILTIN_TYPE_OBJECT, false, $formClass)); + $property->setRef($this->modelRegistry->register($model)); + break; + } } if ($config->getRequired()) { @@ -95,4 +132,9 @@ final class FormModelDescriber implements ModelDescriberInterface } } } + + private function isBuiltinType(string $type): bool + { + return 0 === strpos($type, 'Symfony\Component\Form\Extension\Core\Type'); + } } diff --git a/Tests/Functional/Controller/ApiController.php b/Tests/Functional/Controller/ApiController.php index a3a54e0..1a43d37 100644 --- a/Tests/Functional/Controller/ApiController.php +++ b/Tests/Functional/Controller/ApiController.php @@ -18,6 +18,7 @@ use Nelmio\ApiDocBundle\Annotation\Operation; use Nelmio\ApiDocBundle\Tests\Functional\Entity\Article; use Nelmio\ApiDocBundle\Tests\Functional\Entity\User; use Nelmio\ApiDocBundle\Tests\Functional\Form\DummyType; +use Nelmio\ApiDocBundle\Tests\Functional\Form\UserType; use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route; use Swagger\Annotations as SWG; @@ -71,6 +72,27 @@ class ApiController { } + /** + * @Route("/test/users/{user}", methods={"POST"}, schemes={"https"}, requirements={"user"="/foo/"}) + * @SWG\Response( + * response="201", + * description="Operation automatically detected", + * @Model(type=User::class) + * ) + * @SWG\Parameter( + * name="foo", + * in="body", + * description="This is a parameter", + * @SWG\Schema( + * type="array", + * @Model(type=UserType::class) + * ) + * ) + */ + public function submitUserTypeAction() + { + } + /** * @Route("/test/{user}", methods={"GET"}, schemes={"https"}, requirements={"user"="/foo/"}) * @Operation( diff --git a/Tests/Functional/Form/UserType.php b/Tests/Functional/Form/UserType.php new file mode 100644 index 0000000..8cf8742 --- /dev/null +++ b/Tests/Functional/Form/UserType.php @@ -0,0 +1,32 @@ +add('dummy', DummyType::class); + } + + public function configureOptions(OptionsResolver $resolver) + { + $resolver->setDefaults([ + 'data_class' => User::class, + ]); + } +} diff --git a/Tests/Functional/FunctionalTest.php b/Tests/Functional/FunctionalTest.php index c0e71ec..5d0380e 100644 --- a/Tests/Functional/FunctionalTest.php +++ b/Tests/Functional/FunctionalTest.php @@ -176,6 +176,14 @@ class FunctionalTest extends WebTestCase public function testFormSupport() { + $this->assertEquals([ + 'type' => 'object', + 'properties' => [ + 'dummy' => ['$ref' => '#/definitions/DummyType'], + ], + 'required' => ['dummy'], + ], $this->getModel('UserType')->toArray()); + $this->assertEquals([ 'type' => 'object', 'properties' => [ From d6913dc78f0093d03143ce34491c25695adfb40e Mon Sep 17 00:00:00 2001 From: Jasper Ras Date: Sun, 24 Sep 2017 00:57:06 +0200 Subject: [PATCH 2/5] fix: map route requirements to parameter pattern instead of format (#1081) --- RouteDescriber/RouteMetadataDescriber.php | 2 +- Tests/Functional/FunctionalTest.php | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/RouteDescriber/RouteMetadataDescriber.php b/RouteDescriber/RouteMetadataDescriber.php index 68fe1e6..ed67dd2 100644 --- a/RouteDescriber/RouteMetadataDescriber.php +++ b/RouteDescriber/RouteMetadataDescriber.php @@ -40,7 +40,7 @@ final class RouteMetadataDescriber implements RouteDescriberInterface } if (isset($requirements[$pathVariable])) { - $parameter->setFormat($requirements[$pathVariable]); + $parameter->setPattern($requirements[$pathVariable]); } } } diff --git a/Tests/Functional/FunctionalTest.php b/Tests/Functional/FunctionalTest.php index 5d0380e..f72b5fe 100644 --- a/Tests/Functional/FunctionalTest.php +++ b/Tests/Functional/FunctionalTest.php @@ -113,7 +113,8 @@ class FunctionalTest extends WebTestCase $parameter = $parameters->get('user', 'path'); $this->assertTrue($parameter->getRequired()); $this->assertEquals('string', $parameter->getType()); - $this->assertEquals('/foo/', $parameter->getFormat()); + $this->assertEquals('/foo/', $parameter->getPattern()); + $this->assertEmpty($parameter->getFormat()); } public function testFOSRestAction() From 87f40feefcb3ac95d64d9620011b5b3f4292a957 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tarmo=20Lepp=C3=A4nen?= Date: Wed, 27 Sep 2017 21:29:54 +0300 Subject: [PATCH 3/5] Support for PHP 7.2 (#1094) * Support for PHP 7.2 * Update composer.json --- composer.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/composer.json b/composer.json index 64c6e01..6d10ec7 100644 --- a/composer.json +++ b/composer.json @@ -15,7 +15,7 @@ } ], "require": { - "php": "7.0.*|7.1.*", + "php": "^7.0", "symfony/framework-bundle": "^3.2.5|^4.0", "symfony/property-info": "^3.1|^4.0", "exsyst/swagger": "~0.3", From da1c1e66f8461ae1a498248bd552d8f621f493bd Mon Sep 17 00:00:00 2001 From: tamcy Date: Thu, 12 Oct 2017 12:01:51 +0800 Subject: [PATCH 4/5] iterator_count() should be used against EXSyst\Component\Swagger\Collections\Responses instead of count(). --- Describer/DefaultDescriber.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Describer/DefaultDescriber.php b/Describer/DefaultDescriber.php index 2c803aa..0b7c8ff 100644 --- a/Describer/DefaultDescriber.php +++ b/Describer/DefaultDescriber.php @@ -38,7 +38,7 @@ final class DefaultDescriber implements DescriberInterface $operation = $path->getOperation($method); // Default Response - if (0 === count($operation->getResponses())) { + if (0 === iterator_count($operation->getResponses())) { $defaultResponse = $operation->getResponses()->get('default'); $defaultResponse->setDescription(''); } From 9f2436dce381befa453287f215a1678d6732285c Mon Sep 17 00:00:00 2001 From: Guilhem Niot Date: Wed, 4 Oct 2017 20:59:47 +0200 Subject: [PATCH 5/5] fix tests --- Resources/config/services.xml | 6 +++--- phpunit.xml.dist | 1 + 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/Resources/config/services.xml b/Resources/config/services.xml index dee835c..5606cd4 100644 --- a/Resources/config/services.xml +++ b/Resources/config/services.xml @@ -5,17 +5,17 @@ - + - + - + diff --git a/phpunit.xml.dist b/phpunit.xml.dist index 18979b1..fb133f5 100644 --- a/phpunit.xml.dist +++ b/phpunit.xml.dist @@ -9,6 +9,7 @@ > +