Read discriminator mapping from file configuration (#2034)

* Read discriminator mapping from file configuration

* Use more realistic test data
This commit is contained in:
Emil Masiakowski 2022-09-25 19:56:43 +02:00 committed by GitHub
parent 18425863b7
commit f808eafbe4
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 67 additions and 26 deletions

View File

@ -144,7 +144,7 @@ final class NelmioApiDocExtension extends Extension implements PrependExtensionI
));
$container->getDefinition('nelmio_api_doc.model_describers.object')
->setArgument(3, $config['media_types']);
->setArgument(4, $config['media_types']);
// Add autoconfiguration for model describer
$container->registerForAutoconfiguration(ModelDescriberInterface::class)

View File

@ -23,7 +23,7 @@ use OpenApi\Annotations as OA;
use OpenApi\Generator;
use Symfony\Component\PropertyInfo\PropertyInfoExtractorInterface;
use Symfony\Component\PropertyInfo\Type;
use Symfony\Component\Serializer\Annotation\DiscriminatorMap;
use Symfony\Component\Serializer\Mapping\Factory\ClassMetadataFactoryInterface;
use Symfony\Component\Serializer\NameConverter\NameConverterInterface;
class ObjectModelDescriber implements ModelDescriberInterface, ModelRegistryAwareInterface
@ -33,6 +33,8 @@ class ObjectModelDescriber implements ModelDescriberInterface, ModelRegistryAwar
/** @var PropertyInfoExtractorInterface */
private $propertyInfo;
/** @var ClassMetadataFactoryInterface */
private $classMetadataFactory;
/** @var Reader */
private $doctrineReader;
/** @var PropertyDescriberInterface[] */
@ -46,6 +48,7 @@ class ObjectModelDescriber implements ModelDescriberInterface, ModelRegistryAwar
public function __construct(
PropertyInfoExtractorInterface $propertyInfo,
ClassMetadataFactoryInterface $classMetadataFactory,
Reader $reader,
iterable $propertyDescribers,
array $mediaTypes,
@ -53,6 +56,7 @@ class ObjectModelDescriber implements ModelDescriberInterface, ModelRegistryAwar
bool $useValidationGroups = false
) {
$this->propertyInfo = $propertyInfo;
$this->classMetadataFactory = $classMetadataFactory;
$this->doctrineReader = $reader;
$this->propertyDescribers = $propertyDescribers;
$this->mediaTypes = $mediaTypes;
@ -85,14 +89,17 @@ class ObjectModelDescriber implements ModelDescriberInterface, ModelRegistryAwar
$schema->type = 'object';
$discriminatorMap = $this->getAnnotation($reflClass, DiscriminatorMap::class);
if ($discriminatorMap && Generator::UNDEFINED === $schema->discriminator) {
$mapping = $this->classMetadataFactory
->getMetadataFor($class)
->getClassDiscriminatorMapping();
if ($mapping && Generator::UNDEFINED === $schema->discriminator) {
$this->applyOpenApiDiscriminator(
$model,
$schema,
$this->modelRegistry,
$discriminatorMap->getTypeProperty(),
$discriminatorMap->getMapping()
$mapping->getTypeProperty(),
$mapping->getTypesMapping()
);
}
@ -196,23 +203,6 @@ class ObjectModelDescriber implements ModelDescriberInterface, ModelRegistryAwar
throw new \Exception(sprintf('Type "%s" is not supported in %s::$%s. You may use the `@OA\Property(type="")` annotation to specify it manually.', $types[0]->getBuiltinType(), $model->getType()->getClassName(), $propertyName));
}
/**
* @return mixed
*/
private function getAnnotation(\ReflectionClass $reflection, string $className)
{
if (false === class_exists($className)) {
return null;
}
if (\PHP_VERSION_ID >= 80000) {
if (null !== $attribute = $reflection->getAttributes($className, \ReflectionAttribute::IS_INSTANCEOF)[0] ?? null) {
return $attribute->newInstance();
}
}
return $this->doctrineReader->getClassAnnotation($reflection, $className);
}
public function supports(Model $model): bool
{
return Type::BUILTIN_TYPE_OBJECT === $model->getType()->getBuiltinType()

View File

@ -71,6 +71,7 @@
<!-- Model Describers -->
<service id="nelmio_api_doc.model_describers.object" class="Nelmio\ApiDocBundle\ModelDescriber\ObjectModelDescriber" public="false">
<argument type="service" id="property_info" />
<argument type="service" id="serializer.mapping.class_metadata_factory" />
<argument type="service" id="annotations.reader" />
<argument type="tagged" tag="nelmio_api_doc.object_model.property_describer" />
<argument />

View File

@ -24,6 +24,7 @@ use Nelmio\ApiDocBundle\Tests\Functional\Entity\EntityWithRef;
use Nelmio\ApiDocBundle\Tests\Functional\Entity\SymfonyConstraints;
use Nelmio\ApiDocBundle\Tests\Functional\Entity\SymfonyConstraintsWithValidationGroups;
use Nelmio\ApiDocBundle\Tests\Functional\Entity\SymfonyDiscriminator;
use Nelmio\ApiDocBundle\Tests\Functional\Entity\SymfonyDiscriminatorFileMapping;
use Nelmio\ApiDocBundle\Tests\Functional\Entity\User;
use Nelmio\ApiDocBundle\Tests\Functional\Form\DummyType;
use Nelmio\ApiDocBundle\Tests\Functional\Form\FormWithAlternateSchemaType;
@ -277,6 +278,15 @@ class ApiController80
{
}
/**
* @Route("/discriminator-mapping-configured-with-file", methods={"GET", "POST"})
*
* @OA\Response(response=200, description="Worked well!", @Model(type=SymfonyDiscriminatorFileMapping::class))
*/
public function discriminatorMappingConfiguredWithFileAction()
{
}
/**
* @Route("/named_route-operation-id", name="named_route_operation_id", methods={"GET", "POST"})
*

View File

@ -0,0 +1,16 @@
<?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\Entity;
interface SymfonyDiscriminatorFileMapping
{
}

View File

@ -11,7 +11,7 @@
namespace Nelmio\ApiDocBundle\Tests\Functional\Entity;
class SymfonyDiscriminatorOne extends SymfonyDiscriminator
class SymfonyDiscriminatorOne extends SymfonyDiscriminator implements SymfonyDiscriminatorFileMapping
{
/**
* @var string

View File

@ -11,7 +11,7 @@
namespace Nelmio\ApiDocBundle\Tests\Functional\Entity;
class SymfonyDiscriminatorTwo extends SymfonyDiscriminator
class SymfonyDiscriminatorTwo extends SymfonyDiscriminator implements SymfonyDiscriminatorFileMapping
{
/**
* @var string

View File

@ -622,6 +622,19 @@ class FunctionalTest extends WebTestCase
$this->assertCount(2, $model->oneOf);
}
public function testModelsWithDiscriminatorMapAreLoadedWithOpenApiPolymorphismWhenUsingFileConfiguration()
{
$model = $this->getModel('SymfonyDiscriminatorFileMapping');
$this->assertInstanceOf(OA\Discriminator::class, $model->discriminator);
$this->assertSame('type', $model->discriminator->propertyName);
$this->assertCount(2, $model->discriminator->mapping);
$this->assertArrayHasKey('one', $model->discriminator->mapping);
$this->assertArrayHasKey('two', $model->discriminator->mapping);
$this->assertNotSame(Generator::UNDEFINED, $model->oneOf);
$this->assertCount(2, $model->oneOf);
}
public function testDiscriminatorMapLoadsChildrenModels()
{
// get model does its own assertions

View File

@ -0,0 +1,6 @@
Nelmio\ApiDocBundle\Tests\Functional\Entity\SymfonyDiscriminatorFileMapping:
discriminator_map:
type_property: type
mapping:
one: Nelmio\ApiDocBundle\Tests\Functional\Entity\SymfonyDiscriminatorOne
two: Nelmio\ApiDocBundle\Tests\Functional\Entity\SymfonyDiscriminatorTwo

View File

@ -131,7 +131,12 @@ class TestKernel extends Kernel
'test' => null,
'validation' => null,
'form' => null,
'serializer' => ['enable_annotations' => true],
'serializer' => [
'enable_annotations' => true,
'mapping' => [
'paths' => [__DIR__.'/Resources/serializer/'],
],
],
'property_access' => true,
];
// Support symfony/framework-bundle < 5.4