Merge branch 'v3'

This commit is contained in:
Guilhem Niot 2020-05-30 18:23:49 +02:00
commit 82d766bfe4
7 changed files with 111 additions and 3 deletions

View File

@ -5,6 +5,11 @@ CHANGELOG
----- -----
* Added support of OpenAPI 3.0. The internals were completely reworked and this version introduces BC breaks. * Added support of OpenAPI 3.0. The internals were completely reworked and this version introduces BC breaks.
3.7.0
-----
* Added `@SerializedName` annotation support and name converters when using Symfony >= 4.2.
3.3.0 3.3.0
----- -----

View File

@ -21,6 +21,7 @@ use Nelmio\ApiDocBundle\PropertyDescriber\PropertyDescriberInterface;
use OpenApi\Annotations as OA; use OpenApi\Annotations as OA;
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
{ {
@ -34,6 +35,8 @@ class ObjectModelDescriber implements ModelDescriberInterface, ModelRegistryAwar
private $propertyDescribers; private $propertyDescribers;
/** @var string[] */ /** @var string[] */
private $mediaTypes; private $mediaTypes;
/** @var NameConverterInterface[] */
private $nameConverter;
private $swaggerDefinitionAnnotationReader; private $swaggerDefinitionAnnotationReader;
@ -41,12 +44,14 @@ class ObjectModelDescriber implements ModelDescriberInterface, ModelRegistryAwar
PropertyInfoExtractorInterface $propertyInfo, PropertyInfoExtractorInterface $propertyInfo,
Reader $reader, Reader $reader,
$propertyDescribers, $propertyDescribers,
array $mediaTypes array $mediaTypes,
NameConverterInterface $nameConverter = null
) { ) {
$this->propertyInfo = $propertyInfo; $this->propertyInfo = $propertyInfo;
$this->doctrineReader = $reader; $this->doctrineReader = $reader;
$this->propertyDescribers = $propertyDescribers; $this->propertyDescribers = $propertyDescribers;
$this->mediaTypes = $mediaTypes; $this->mediaTypes = $mediaTypes;
$this->nameConverter = $nameConverter;
} }
public function describe(Model $model, OA\Schema $schema) public function describe(Model $model, OA\Schema $schema)
@ -70,10 +75,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 OpenApi Property annotation if it exists // read property options from OpenApi 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 = Util::getProperty($schema, $annotationsReader->getPropertyName($reflectionProperty, $propertyName)); $property = Util::getProperty($schema, $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])) {
@ -82,7 +89,7 @@ class ObjectModelDescriber implements ModelDescriberInterface, ModelRegistryAwar
$annotationsReader->updateProperty($reflectionProperty, $property, $groups); $annotationsReader->updateProperty($reflectionProperty, $property, $groups);
} else { } else {
$property = Util::getProperty($schema, $propertyName); $property = Util::getProperty($schema, $serializedName);
} }
// If type manually defined // If type manually defined

View File

@ -43,6 +43,7 @@
<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 /> <argument />
<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 OpenApi\Annotations as OA;
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
{
/**
* @OA\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

@ -13,6 +13,7 @@ namespace Nelmio\ApiDocBundle\Tests\Functional;
use Nelmio\ApiDocBundle\OpenApiPhp\Util; use Nelmio\ApiDocBundle\OpenApiPhp\Util;
use OpenApi\Annotations as OA; use OpenApi\Annotations as OA;
use Symfony\Component\Serializer\Annotation\SerializedName;
class FunctionalTest extends WebTestCase class FunctionalTest extends WebTestCase
{ {
@ -409,4 +410,20 @@ class FunctionalTest extends WebTestCase
{ {
$this->assertNotHasParameter('name', 'path', $this->getOperation('/api/article/{id}', 'get')); $this->assertNotHasParameter('name', 'path', $this->getOperation('/api/article/{id}', 'get'));
} }
public function testSerializedNameAction()
{
if (!class_exists(SerializedName::class)) {
$this->markTestSkipped('Annotation @SerializedName doesn\'t exist.');
}
$model = $this->getModel('SerializedNameEnt');
$this->assertCount(2, $model->properties);
$this->assertNotHasProperty('foo', $model);
$this->assertHasProperty('notfoo', $model);
$this->assertNotHasProperty('bar', $model);
$this->assertHasProperty('notwhatyouthink', $model);
}
} }

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');
} }