Add @SerializedName support (#1636)

* Add `@SerializedName` support

* Also test `@SerializedName` on properties

* Fix tests with sf < 4.2

* Fix the tests

* Update the CHANGELOG
This commit is contained in:
Guilhem Niot 2020-05-30 18:08:25 +02:00 committed by GitHub
parent 5e8f7290e8
commit dfff2476d8
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 113 additions and 5 deletions

View File

@ -1,10 +1,15 @@
CHANGELOG 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. 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). 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).

View File

@ -20,6 +20,7 @@ use Nelmio\ApiDocBundle\ModelDescriber\Annotations\AnnotationsReader;
use Nelmio\ApiDocBundle\PropertyDescriber\PropertyDescriberInterface; use Nelmio\ApiDocBundle\PropertyDescriber\PropertyDescriberInterface;
use Symfony\Component\PropertyInfo\PropertyInfoExtractorInterface; use Symfony\Component\PropertyInfo\PropertyInfoExtractorInterface;
use Symfony\Component\PropertyInfo\Type; use Symfony\Component\PropertyInfo\Type;
use Symfony\Component\Serializer\NameConverter\NameConverterInterface;
class ObjectModelDescriber implements ModelDescriberInterface, ModelRegistryAwareInterface class ObjectModelDescriber implements ModelDescriberInterface, ModelRegistryAwareInterface
{ {
@ -31,17 +32,21 @@ class ObjectModelDescriber implements ModelDescriberInterface, ModelRegistryAwar
private $doctrineReader; private $doctrineReader;
/** @var PropertyDescriberInterface[] */ /** @var PropertyDescriberInterface[] */
private $propertyDescribers; private $propertyDescribers;
/** @var NameConverterInterface[] */
private $nameConverter;
private $swaggerDefinitionAnnotationReader; private $swaggerDefinitionAnnotationReader;
public function __construct( public function __construct(
PropertyInfoExtractorInterface $propertyInfo, PropertyInfoExtractorInterface $propertyInfo,
Reader $reader, Reader $reader,
$propertyDescribers $propertyDescribers,
NameConverterInterface $nameConverter = null
) { ) {
$this->propertyInfo = $propertyInfo; $this->propertyInfo = $propertyInfo;
$this->doctrineReader = $reader; $this->doctrineReader = $reader;
$this->propertyDescribers = $propertyDescribers; $this->propertyDescribers = $propertyDescribers;
$this->nameConverter = $nameConverter;
} }
public function describe(Model $model, Schema $schema) public function describe(Model $model, Schema $schema)
@ -64,10 +69,12 @@ class ObjectModelDescriber implements ModelDescriberInterface, ModelRegistryAwar
} }
foreach ($propertyInfoProperties as $propertyName) { 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 // read property options from Swagger Property annotation if it exists
if (property_exists($class, $propertyName)) { if (property_exists($class, $propertyName)) {
$reflectionProperty = new \ReflectionProperty($class, $propertyName); $reflectionProperty = new \ReflectionProperty($class, $propertyName);
$property = $properties->get($annotationsReader->getPropertyName($reflectionProperty, $propertyName)); $property = $properties->get($annotationsReader->getPropertyName($reflectionProperty, $serializedName));
$groups = $model->getGroups(); $groups = $model->getGroups();
if (isset($groups[$propertyName]) && is_array($groups[$propertyName])) { if (isset($groups[$propertyName]) && is_array($groups[$propertyName])) {
@ -76,7 +83,7 @@ class ObjectModelDescriber implements ModelDescriberInterface, ModelRegistryAwar
$annotationsReader->updateProperty($reflectionProperty, $property, $groups); $annotationsReader->updateProperty($reflectionProperty, $property, $groups);
} else { } else {
$property = $properties->get($propertyName); $property = $properties->get($serializedName);
} }
// If type manually defined // If type manually defined

View File

@ -42,6 +42,7 @@
<argument type="service" id="property_info" /> <argument type="service" id="property_info" />
<argument type="service" id="annotation_reader" /> <argument type="service" id="annotation_reader" />
<argument type="tagged" tag="nelmio_api_doc.object_model.property_describer" /> <argument type="tagged" tag="nelmio_api_doc.object_model.property_describer" />
<argument type="service" id="serializer.name_converter.metadata_aware" on-invalid="ignore" />
<tag name="nelmio_api_doc.model_describer" /> <tag name="nelmio_api_doc.model_describer" />
</service> </service>

View File

@ -0,0 +1,37 @@
<?php
/*
* This file is part of the NelmioApiDocBundle package.
*
* (c) Nelmio
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Nelmio\ApiDocBundle\Tests\Functional\Controller;
use Nelmio\ApiDocBundle\Annotation\Model;
use Nelmio\ApiDocBundle\Tests\Functional\EntityExcluded\SerializedNameEnt;
use Swagger\Annotations as SWG;
use Symfony\Component\Routing\Annotation\Route;
/**
* This controller is only loaded when SerializedName exists (sf >= 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()
{
}
}

View File

@ -0,0 +1,36 @@
<?php
/*
* This file is part of the NelmioApiDocBundle package.
*
* (c) Nelmio
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Nelmio\ApiDocBundle\Tests\Functional\EntityExcluded;
use Symfony\Component\Serializer\Annotation\SerializedName;
/**
* @author Guilhem N. <guilhem.niot@gmail.com>
*/
class SerializedNameEnt
{
/**
* @SerializedName("notfoo")
*
* @var string
*/
public $foo;
/**
* Tests serialized name feature.
*
* @SerializedName("notwhatyouthink")
*/
public function setBar(string $bar)
{
}
}

View File

@ -12,6 +12,7 @@
namespace Nelmio\ApiDocBundle\Tests\Functional; namespace Nelmio\ApiDocBundle\Tests\Functional;
use EXSyst\Component\Swagger\Tag; use EXSyst\Component\Swagger\Tag;
use Symfony\Component\Serializer\Annotation\SerializedName;
class FunctionalTest extends WebTestCase class FunctionalTest extends WebTestCase
{ {
@ -407,4 +408,20 @@ class FunctionalTest extends WebTestCase
{ {
$this->assertFalse($this->getOperation('/api/article/{id}', 'get')->getParameters()->has('id', 'path')); $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'));
}
} }

View File

@ -29,6 +29,7 @@ use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\Definition; use Symfony\Component\DependencyInjection\Definition;
use Symfony\Component\HttpKernel\Kernel; use Symfony\Component\HttpKernel\Kernel;
use Symfony\Component\Routing\RouteCollectionBuilder; use Symfony\Component\Routing\RouteCollectionBuilder;
use Symfony\Component\Serializer\Annotation\SerializedName;
class TestKernel extends Kernel 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/{area}', 'nelmio_api_doc.controller.swagger_ui')->setDefault('area', 'default');
$routes->add('/docs.json', 'nelmio_api_doc.controller.swagger'); $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)) { if (class_exists(FOSRestBundle::class)) {
$routes->import(__DIR__.'/Controller/FOSRestController.php', '/', 'annotation'); $routes->import(__DIR__.'/Controller/FOSRestController.php', '/', 'annotation');
} }