diff --git a/src/Bridge/NelmioApiDoc/Parser/ApiPlatformParser.php b/src/Bridge/NelmioApiDoc/Parser/ApiPlatformParser.php
index a5a680bd9c7..9a802518252 100644
--- a/src/Bridge/NelmioApiDoc/Parser/ApiPlatformParser.php
+++ b/src/Bridge/NelmioApiDoc/Parser/ApiPlatformParser.php
@@ -20,6 +20,7 @@
use Nelmio\ApiDocBundle\DataTypes;
use Nelmio\ApiDocBundle\Parser\ParserInterface;
use Symfony\Component\PropertyInfo\Type;
+use Symfony\Component\Serializer\NameConverter\NameConverterInterface;
/**
* Extract input and output information for the NelmioApiDocBundle.
@@ -42,12 +43,14 @@ final class ApiPlatformParser implements ParserInterface
private $resourceMetadataFactory;
private $propertyNameCollectionFactory;
private $propertyMetadataFactory;
+ private $nameConverter;
- public function __construct(ResourceMetadataFactoryInterface $resourceMetadataFactory, PropertyNameCollectionFactoryInterface $propertyNameCollectionFactory, PropertyMetadataFactoryInterface $propertyMetadataFactory)
+ public function __construct(ResourceMetadataFactoryInterface $resourceMetadataFactory, PropertyNameCollectionFactoryInterface $propertyNameCollectionFactory, PropertyMetadataFactoryInterface $propertyMetadataFactory, NameConverterInterface $nameConverter = null)
{
$this->resourceMetadataFactory = $resourceMetadataFactory;
$this->propertyNameCollectionFactory = $propertyNameCollectionFactory;
$this->propertyMetadataFactory = $propertyMetadataFactory;
+ $this->nameConverter = $nameConverter;
}
/**
@@ -171,7 +174,8 @@ private function getPropertyMetadata(ResourceMetadata $resourceMetadata, string
($propertyMetadata->isReadable() && self::OUT_PREFIX === $io) ||
($propertyMetadata->isWritable() && self::IN_PREFIX === $io)
) {
- $data[$propertyName] = $this->parseProperty($resourceMetadata, $propertyMetadata, $io, null, $visited);
+ $normalizedPropertyName = $this->nameConverter ? $this->nameConverter->normalize($propertyName) : $propertyName;
+ $data[$normalizedPropertyName] = $this->parseProperty($resourceMetadata, $propertyMetadata, $io, null, $visited);
}
}
diff --git a/src/Bridge/Symfony/Bundle/Resources/config/nelmio_api_doc.xml b/src/Bridge/Symfony/Bundle/Resources/config/nelmio_api_doc.xml
index 141b950f233..980757408e2 100644
--- a/src/Bridge/Symfony/Bundle/Resources/config/nelmio_api_doc.xml
+++ b/src/Bridge/Symfony/Bundle/Resources/config/nelmio_api_doc.xml
@@ -19,6 +19,7 @@
+
diff --git a/src/Bridge/Symfony/Bundle/Resources/config/swagger.xml b/src/Bridge/Symfony/Bundle/Resources/config/swagger.xml
index 42c535d87a2..2b8480d0c52 100644
--- a/src/Bridge/Symfony/Bundle/Resources/config/swagger.xml
+++ b/src/Bridge/Symfony/Bundle/Resources/config/swagger.xml
@@ -15,6 +15,7 @@
+
diff --git a/src/Swagger/Serializer/DocumentationNormalizer.php b/src/Swagger/Serializer/DocumentationNormalizer.php
index a50a45776d1..136013e1fe5 100644
--- a/src/Swagger/Serializer/DocumentationNormalizer.php
+++ b/src/Swagger/Serializer/DocumentationNormalizer.php
@@ -23,6 +23,7 @@
use ApiPlatform\Core\Metadata\Resource\ResourceMetadata;
use ApiPlatform\Core\PathResolver\OperationPathResolverInterface;
use Symfony\Component\PropertyInfo\Type;
+use Symfony\Component\Serializer\NameConverter\NameConverterInterface;
use Symfony\Component\Serializer\Normalizer\NormalizerInterface;
/**
@@ -45,8 +46,9 @@ final class DocumentationNormalizer implements NormalizerInterface
private $operationPathResolver;
private $urlGenerator;
private $filterCollection;
+ private $nameConverter;
- public function __construct(ResourceMetadataFactoryInterface $resourceMetadataFactory, PropertyNameCollectionFactoryInterface $propertyNameCollectionFactory, PropertyMetadataFactoryInterface $propertyMetadataFactory, ResourceClassResolverInterface $resourceClassResolver, OperationMethodResolverInterface $operationMethodResolver, OperationPathResolverInterface $operationPathResolver, UrlGeneratorInterface $urlGenerator, FilterCollection $filterCollection = null)
+ public function __construct(ResourceMetadataFactoryInterface $resourceMetadataFactory, PropertyNameCollectionFactoryInterface $propertyNameCollectionFactory, PropertyMetadataFactoryInterface $propertyMetadataFactory, ResourceClassResolverInterface $resourceClassResolver, OperationMethodResolverInterface $operationMethodResolver, OperationPathResolverInterface $operationPathResolver, UrlGeneratorInterface $urlGenerator, FilterCollection $filterCollection = null, NameConverterInterface $nameConverter = null)
{
$this->resourceMetadataFactory = $resourceMetadataFactory;
$this->propertyNameCollectionFactory = $propertyNameCollectionFactory;
@@ -56,6 +58,7 @@ public function __construct(ResourceMetadataFactoryInterface $resourceMetadataFa
$this->operationPathResolver = $operationPathResolver;
$this->urlGenerator = $urlGenerator;
$this->filterCollection = $filterCollection;
+ $this->nameConverter = $nameConverter;
}
/**
@@ -378,12 +381,13 @@ private function getDefinitionSchema(string $resourceClass, ResourceMetadata $re
$options = isset($serializerContext['groups']) ? ['serializer_groups' => $serializerContext['groups']] : [];
foreach ($this->propertyNameCollectionFactory->create($resourceClass, $options) as $propertyName) {
$propertyMetadata = $this->propertyMetadataFactory->create($resourceClass, $propertyName);
+ $normalizedPropertyName = $this->nameConverter ? $this->nameConverter->normalize($propertyName) : $propertyName;
if ($propertyMetadata->isRequired()) {
- $definitionSchema['required'][] = $propertyName;
+ $definitionSchema['required'][] = $normalizedPropertyName;
}
- $definitionSchema['properties'][$propertyName] = $this->getPropertySchema($propertyMetadata);
+ $definitionSchema['properties'][$normalizedPropertyName] = $this->getPropertySchema($propertyMetadata);
}
return $definitionSchema;
diff --git a/tests/Bridge/NelmioApiDoc/Parser/ApiPlatformParserTest.php b/tests/Bridge/NelmioApiDoc/Parser/ApiPlatformParserTest.php
index a66ef1551f1..297788fb1d6 100644
--- a/tests/Bridge/NelmioApiDoc/Parser/ApiPlatformParserTest.php
+++ b/tests/Bridge/NelmioApiDoc/Parser/ApiPlatformParserTest.php
@@ -26,6 +26,7 @@
use Nelmio\ApiDocBundle\Parser\ParserInterface;
use Prophecy\Argument;
use Symfony\Component\PropertyInfo\Type;
+use Symfony\Component\Serializer\NameConverter\NameConverterInterface;
/**
* @author Teoh Han Hui
@@ -359,4 +360,46 @@ public function testParseRelation()
],
], $actual);
}
+
+ public function testParseWithNameConverter()
+ {
+ $resourceMetadataFactoryProphecy = $this->prophesize(ResourceMetadataFactoryInterface::class);
+ $resourceMetadataFactoryProphecy->create(Dummy::class)->willReturn(new ResourceMetadata('dummy', 'dummy', null, [], []))->shouldBeCalled();
+ $resourceMetadataFactory = $resourceMetadataFactoryProphecy->reveal();
+
+ $propertyNameCollectionFactoryProphecy = $this->prophesize(PropertyNameCollectionFactoryInterface::class);
+ $propertyNameCollectionFactoryProphecy->create(Dummy::class, Argument::cetera())->willReturn(new PropertyNameCollection([
+ 'nameConverted',
+ ]))->shouldBeCalled();
+ $propertyNameCollectionFactory = $propertyNameCollectionFactoryProphecy->reveal();
+
+ $propertyMetadataFactoryProphecy = $this->prophesize(PropertyMetadataFactoryInterface::class);
+ $nameConvertedPropertyMetadata = (new PropertyMetadata())
+ ->withType(new Type(Type::BUILTIN_TYPE_STRING, true))
+ ->withDescription('A converted name')
+ ->withReadable(true)
+ ->withWritable(true)
+ ->withRequired(false);
+ $propertyMetadataFactoryProphecy->create(Dummy::class, 'nameConverted')->willReturn($nameConvertedPropertyMetadata)->shouldBeCalled();
+ $propertyMetadataFactory = $propertyMetadataFactoryProphecy->reveal();
+
+ $nameConverterProphecy = $this->prophesize(NameConverterInterface::class);
+ $nameConverterProphecy->normalize('nameConverted')->willReturn('name_converted')->shouldBeCalled();
+ $nameConverter = $nameConverterProphecy->reveal();
+
+ $apiPlatformParser = new ApiPlatformParser($resourceMetadataFactory, $propertyNameCollectionFactory, $propertyMetadataFactory, $nameConverter);
+
+ $actual = $apiPlatformParser->parse([
+ 'class' => sprintf('%s:%s:%s', ApiPlatformParser::OUT_PREFIX, Dummy::class, 'get'),
+ ]);
+
+ $this->assertEquals([
+ 'name_converted' => [
+ 'dataType' => DataTypes::STRING,
+ 'required' => false,
+ 'description' => 'A converted name',
+ 'readonly' => false,
+ ],
+ ], $actual);
+ }
}
diff --git a/tests/Swagger/Serializer/DocumentationNormalizerTest.php b/tests/Swagger/Serializer/DocumentationNormalizerTest.php
index b3bafc545c4..66871f49793 100644
--- a/tests/Swagger/Serializer/DocumentationNormalizerTest.php
+++ b/tests/Swagger/Serializer/DocumentationNormalizerTest.php
@@ -29,6 +29,7 @@
use ApiPlatform\Core\Tests\Fixtures\DummyFilter;
use ApiPlatform\Core\Tests\Fixtures\TestBundle\Entity\Dummy;
use Symfony\Component\PropertyInfo\Type;
+use Symfony\Component\Serializer\NameConverter\NameConverterInterface;
/**
* @author Amrouche Hamza
@@ -235,6 +236,103 @@ public function testNormalize()
$this->assertEquals($expected, $normalizer->normalize($documentation));
}
+ public function testNormalizeWithNameConverter()
+ {
+ $documentation = new Documentation(new ResourceNameCollection([Dummy::class]), 'Dummy API', 'This is a dummy API', '1.2.3', ['jsonld' => ['application/ld+json']]);
+
+ $propertyNameCollectionFactoryProphecy = $this->prophesize(PropertyNameCollectionFactoryInterface::class);
+ $propertyNameCollectionFactoryProphecy->create(Dummy::class, [])->shouldBeCalled()->willReturn(new PropertyNameCollection(['name', 'nameConverted']));
+
+ $dummyMetadata = new ResourceMetadata('Dummy', 'This is a dummy.', null, ['get' => ['method' => 'GET']], [], []);
+
+ $resourceMetadataFactoryProphecy = $this->prophesize(ResourceMetadataFactoryInterface::class);
+ $resourceMetadataFactoryProphecy->create(Dummy::class)->shouldBeCalled()->willReturn($dummyMetadata);
+
+ $propertyMetadataFactoryProphecy = $this->prophesize(PropertyMetadataFactoryInterface::class);
+ $propertyMetadataFactoryProphecy->create(Dummy::class, 'name')->shouldBeCalled()->willReturn(new PropertyMetadata(new Type(Type::BUILTIN_TYPE_STRING), 'This is a name.', true, true, null, null, false));
+ $propertyMetadataFactoryProphecy->create(Dummy::class, 'nameConverted')->shouldBeCalled()->willReturn(new PropertyMetadata(new Type(Type::BUILTIN_TYPE_STRING), 'This is a converted name.', true, true, null, null, false));
+
+ $resourceClassResolverProphecy = $this->prophesize(ResourceClassResolverInterface::class);
+ $resourceClassResolverProphecy->isResourceClass(Dummy::class)->willReturn(true);
+
+ $operationMethodResolverProphecy = $this->prophesize(OperationMethodResolverInterface::class);
+ $operationMethodResolverProphecy->getItemOperationMethod(Dummy::class, 'get')->shouldBeCalled()->willReturn('GET');
+
+ $urlGeneratorProphecy = $this->prophesize(UrlGeneratorInterface::class);
+ $urlGeneratorProphecy->generate('api_entrypoint')->willReturn('/app_dev.php/')->shouldBeCalled();
+
+ $nameConverterProphecy = $this->prophesize(NameConverterInterface::class);
+ $nameConverterProphecy->normalize('name')->willReturn('name')->shouldBeCalled();
+ $nameConverterProphecy->normalize('nameConverted')->willReturn('name_converted')->shouldBeCalled();
+
+ $operationPathResolver = new CustomOperationPathResolver(new UnderscoreOperationPathResolver());
+
+ $normalizer = new DocumentationNormalizer(
+ $resourceMetadataFactoryProphecy->reveal(),
+ $propertyNameCollectionFactoryProphecy->reveal(),
+ $propertyMetadataFactoryProphecy->reveal(),
+ $resourceClassResolverProphecy->reveal(),
+ $operationMethodResolverProphecy->reveal(),
+ $operationPathResolver,
+ $urlGeneratorProphecy->reveal(),
+ null,
+ $nameConverterProphecy->reveal()
+ );
+
+ $expected = [
+ 'swagger' => '2.0',
+ 'basePath' => '/app_dev.php/',
+ 'info' => [
+ 'title' => 'Dummy API',
+ 'description' => 'This is a dummy API',
+ 'version' => '1.2.3',
+ ],
+ 'paths' => new \ArrayObject([
+ '/dummies/{id}' => [
+ 'get' => new \ArrayObject([
+ 'tags' => ['Dummy'],
+ 'operationId' => 'getDummyItem',
+ 'produces' => ['application/ld+json'],
+ 'summary' => 'Retrieves a Dummy resource.',
+ 'parameters' => [
+ [
+ 'name' => 'id',
+ 'in' => 'path',
+ 'type' => 'integer',
+ 'required' => true,
+ ],
+ ],
+ 'responses' => [
+ 200 => [
+ 'description' => 'Dummy resource response',
+ 'schema' => ['$ref' => '#/definitions/Dummy'],
+ ],
+ 404 => ['description' => 'Resource not found'],
+ ],
+ ]),
+ ],
+ ]),
+ 'definitions' => new \ArrayObject([
+ 'Dummy' => new \ArrayObject([
+ 'type' => 'object',
+ 'description' => 'This is a dummy.',
+ 'properties' => [
+ 'name' => new \ArrayObject([
+ 'type' => 'string',
+ 'description' => 'This is a name.',
+ ]),
+ 'name_converted' => new \ArrayObject([
+ 'type' => 'string',
+ 'description' => 'This is a converted name.',
+ ]),
+ ],
+ ]),
+ ]),
+ ];
+
+ $this->assertEquals($expected, $normalizer->normalize($documentation));
+ }
+
public function testNormalizeWithOnlyNormalizationGroups()
{
$title = 'Test API';