From dfff2476d8cb2f80b98ec20109324c08f69dd0f7 Mon Sep 17 00:00:00 2001 From: Guilhem Niot Date: Sat, 30 May 2020 18:08:25 +0200 Subject: [PATCH] Add `@SerializedName` support (#1636) * Add `@SerializedName` support * Also test `@SerializedName` on properties * Fix tests with sf < 4.2 * Fix the tests * Update the CHANGELOG --- CHANGELOG.md | 9 ++++- ModelDescriber/ObjectModelDescriber.php | 13 +++++-- Resources/config/services.xml | 1 + .../Controller/SerializedNameController.php | 37 +++++++++++++++++++ .../EntityExcluded/SerializedNameEnt.php | 36 ++++++++++++++++++ Tests/Functional/FunctionalTest.php | 17 +++++++++ Tests/Functional/TestKernel.php | 5 +++ 7 files changed, 113 insertions(+), 5 deletions(-) create mode 100644 Tests/Functional/Controller/SerializedNameController.php create mode 100644 Tests/Functional/EntityExcluded/SerializedNameEnt.php diff --git a/CHANGELOG.md b/CHANGELOG.md index 41334a3..08efefd 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,10 +1,15 @@ CHANGELOG ========= -3.3.0 (unreleased) +3.7.0 (unreleased) ------------------ -* Usage of Google Fonts was removed. System fonts `serif` / `sans` will be used instead. +* Added `@SerializedName` annotation support and name converters when using Symfony >= 4.2. + +3.3.0 +----- + +* Usage of Google Fonts was removed. System fonts `serif` / `sans` will be used instead. This can lead to a different look on different operating systems. You can [re-add Google Fonts again manually by overriding the template](https://symfony.com/doc/current/bundles/NelmioApiDocBundle/faq.html#re-add-google-fonts). diff --git a/ModelDescriber/ObjectModelDescriber.php b/ModelDescriber/ObjectModelDescriber.php index d5f293c..db373cc 100644 --- a/ModelDescriber/ObjectModelDescriber.php +++ b/ModelDescriber/ObjectModelDescriber.php @@ -20,6 +20,7 @@ use Nelmio\ApiDocBundle\ModelDescriber\Annotations\AnnotationsReader; use Nelmio\ApiDocBundle\PropertyDescriber\PropertyDescriberInterface; use Symfony\Component\PropertyInfo\PropertyInfoExtractorInterface; use Symfony\Component\PropertyInfo\Type; +use Symfony\Component\Serializer\NameConverter\NameConverterInterface; class ObjectModelDescriber implements ModelDescriberInterface, ModelRegistryAwareInterface { @@ -31,17 +32,21 @@ class ObjectModelDescriber implements ModelDescriberInterface, ModelRegistryAwar private $doctrineReader; /** @var PropertyDescriberInterface[] */ private $propertyDescribers; + /** @var NameConverterInterface[] */ + private $nameConverter; private $swaggerDefinitionAnnotationReader; public function __construct( PropertyInfoExtractorInterface $propertyInfo, Reader $reader, - $propertyDescribers + $propertyDescribers, + NameConverterInterface $nameConverter = null ) { $this->propertyInfo = $propertyInfo; $this->doctrineReader = $reader; $this->propertyDescribers = $propertyDescribers; + $this->nameConverter = $nameConverter; } public function describe(Model $model, Schema $schema) @@ -64,10 +69,12 @@ class ObjectModelDescriber implements ModelDescriberInterface, ModelRegistryAwar } foreach ($propertyInfoProperties as $propertyName) { + $serializedName = null !== $this->nameConverter ? $this->nameConverter->normalize($propertyName, $class, null, null !== $model->getGroups() ? ['groups' => $model->getGroups()] : []) : $propertyName; + // read property options from Swagger Property annotation if it exists if (property_exists($class, $propertyName)) { $reflectionProperty = new \ReflectionProperty($class, $propertyName); - $property = $properties->get($annotationsReader->getPropertyName($reflectionProperty, $propertyName)); + $property = $properties->get($annotationsReader->getPropertyName($reflectionProperty, $serializedName)); $groups = $model->getGroups(); if (isset($groups[$propertyName]) && is_array($groups[$propertyName])) { @@ -76,7 +83,7 @@ class ObjectModelDescriber implements ModelDescriberInterface, ModelRegistryAwar $annotationsReader->updateProperty($reflectionProperty, $property, $groups); } else { - $property = $properties->get($propertyName); + $property = $properties->get($serializedName); } // If type manually defined diff --git a/Resources/config/services.xml b/Resources/config/services.xml index c575d42..540da54 100644 --- a/Resources/config/services.xml +++ b/Resources/config/services.xml @@ -42,6 +42,7 @@ + diff --git a/Tests/Functional/Controller/SerializedNameController.php b/Tests/Functional/Controller/SerializedNameController.php new file mode 100644 index 0000000..bafa7ec --- /dev/null +++ b/Tests/Functional/Controller/SerializedNameController.php @@ -0,0 +1,37 @@ += 4.2). + * + * @Route("/api", host="api.example.com") + */ +class SerializedNameController +{ + /** + * @SWG\Response( + * response="200", + * description="success", + * @Model(type=SerializedNameEnt::class) + * ) + * @Route("/serializename", methods={"GET"}) + */ + public function serializedNameAction() + { + } +} diff --git a/Tests/Functional/EntityExcluded/SerializedNameEnt.php b/Tests/Functional/EntityExcluded/SerializedNameEnt.php new file mode 100644 index 0000000..a7dc459 --- /dev/null +++ b/Tests/Functional/EntityExcluded/SerializedNameEnt.php @@ -0,0 +1,36 @@ + + */ +class SerializedNameEnt +{ + /** + * @SerializedName("notfoo") + * + * @var string + */ + public $foo; + + /** + * Tests serialized name feature. + * + * @SerializedName("notwhatyouthink") + */ + public function setBar(string $bar) + { + } +} diff --git a/Tests/Functional/FunctionalTest.php b/Tests/Functional/FunctionalTest.php index 138aaa7..0e18c3f 100644 --- a/Tests/Functional/FunctionalTest.php +++ b/Tests/Functional/FunctionalTest.php @@ -12,6 +12,7 @@ namespace Nelmio\ApiDocBundle\Tests\Functional; use EXSyst\Component\Swagger\Tag; +use Symfony\Component\Serializer\Annotation\SerializedName; class FunctionalTest extends WebTestCase { @@ -407,4 +408,20 @@ class FunctionalTest extends WebTestCase { $this->assertFalse($this->getOperation('/api/article/{id}', 'get')->getParameters()->has('id', 'path')); } + + public function testSerializedNameAction() + { + if (!class_exists(SerializedName::class)) { + $this->markTestSkipped('Annotation @SerializedName doesn\'t exist.'); + } + + $modelProperties = $this->getModel('SerializedNameEnt')->getProperties(); + $this->assertCount(2, $modelProperties); + + $this->assertFalse($modelProperties->has('foo')); + $this->assertTrue($modelProperties->has('notfoo')); + + $this->assertFalse($modelProperties->has('bar')); + $this->assertTrue($modelProperties->has('notwhatyouthink')); + } } diff --git a/Tests/Functional/TestKernel.php b/Tests/Functional/TestKernel.php index 761478c..a51526c 100644 --- a/Tests/Functional/TestKernel.php +++ b/Tests/Functional/TestKernel.php @@ -29,6 +29,7 @@ use Symfony\Component\DependencyInjection\ContainerBuilder; use Symfony\Component\DependencyInjection\Definition; use Symfony\Component\HttpKernel\Kernel; use Symfony\Component\Routing\RouteCollectionBuilder; +use Symfony\Component\Serializer\Annotation\SerializedName; class TestKernel extends Kernel { @@ -87,6 +88,10 @@ class TestKernel extends Kernel $routes->add('/docs/{area}', 'nelmio_api_doc.controller.swagger_ui')->setDefault('area', 'default'); $routes->add('/docs.json', 'nelmio_api_doc.controller.swagger'); + if (class_exists(SerializedName::class)) { + $routes->import(__DIR__.'/Controller/SerializedNameController.php', '/', 'annotation'); + } + if (class_exists(FOSRestBundle::class)) { $routes->import(__DIR__.'/Controller/FOSRestController.php', '/', 'annotation'); }