Skip to content

Commit

Permalink
feat: pass serialization context to name converter (#2167)
Browse files Browse the repository at this point in the history
* feat: pass serialization context to name converter

* fix: cs

* fix: ci php 7.3 failing

* fix: ci php 7.3 failing

* fix: ci, decorate name converter

* fix: ci, decorate name converter

* move NameConverter test logic to FunctionTest

* fix baseline

* remove unnecessary spaces
  • Loading branch information
DjordyKoert committed Jan 2, 2024
1 parent 9ff1d64 commit c55d9ef
Show file tree
Hide file tree
Showing 12 changed files with 140 additions and 15 deletions.
16 changes: 12 additions & 4 deletions Annotation/Model.php
Original file line number Diff line number Diff line change
Expand Up @@ -50,20 +50,28 @@ final class Model extends Attachable
public $options;

/**
* @param mixed[] $properties
* @param string[] $groups
* @param mixed[] $options
* @var array<string, mixed>
*/
public $serializationContext;

/**
* @param mixed[] $properties
* @param string[] $groups
* @param mixed[] $options
* @param array<string, mixed> $serializationContext
*/
public function __construct(
array $properties = [],
string $type = Generator::UNDEFINED,
array $groups = null,
array $options = null
array $options = null,
array $serializationContext = []
) {
parent::__construct($properties + [
'type' => $type,
'groups' => $groups,
'options' => $options,
'serializationContext' => $serializationContext,
]);
}
}
23 changes: 17 additions & 6 deletions Model/Model.php
Original file line number Diff line number Diff line change
Expand Up @@ -12,23 +12,26 @@
namespace Nelmio\ApiDocBundle\Model;

use Symfony\Component\PropertyInfo\Type;
use Symfony\Component\Serializer\Normalizer\AbstractNormalizer;

final class Model
{
private $type;

private $groups;

private $options;
private $serializationContext;

/**
* @param string[]|null $groups
*/
public function __construct(Type $type, array $groups = null, array $options = null)
public function __construct(Type $type, array $groups = null, array $options = null, array $serializationContext = [])
{
$this->type = $type;
$this->groups = $groups;
$this->options = $options;
$this->serializationContext = $serializationContext;
if (null !== $groups) {
$this->serializationContext[AbstractNormalizer::GROUPS] = $groups;
}
}

/**
Expand All @@ -44,12 +47,20 @@ public function getType()
*/
public function getGroups()
{
return $this->groups;
return $this->serializationContext[AbstractNormalizer::GROUPS] ?? null;
}

/**
* @return array<string, mixed>
*/
public function getSerializationContext(): array
{
return $this->serializationContext;
}

public function getHash(): string
{
return md5(serialize([$this->type, $this->groups]));
return md5(serialize([$this->type, $this->getGroups()]));
}

/**
Expand Down
2 changes: 1 addition & 1 deletion ModelDescriber/ObjectModelDescriber.php
Original file line number Diff line number Diff line change
Expand Up @@ -117,7 +117,7 @@ public function describe(Model $model, OA\Schema $schema)
$propertyInfoProperties = array_intersect($propertyInfoProperties, $this->propertyInfo->getProperties($class, []) ?? []);

foreach ($propertyInfoProperties as $propertyName) {
$serializedName = null !== $this->nameConverter ? $this->nameConverter->normalize($propertyName, $class, null, null !== $model->getGroups() ? ['groups' => $model->getGroups()] : []) : $propertyName;
$serializedName = null !== $this->nameConverter ? $this->nameConverter->normalize($propertyName, $class, null, $model->getSerializationContext()) : $propertyName;

$reflections = $this->getReflections($reflClass, $propertyName);

Expand Down
6 changes: 3 additions & 3 deletions OpenApiPhp/ModelRegister.php
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ public function __invoke(Analysis $analysis, array $parentGroups = null)
if ($annotation instanceof OA\Schema && $annotation->ref instanceof ModelAnnotation) {
$model = $annotation->ref;

$annotation->ref = $this->modelRegistry->register(new Model($this->createType($model->type), $this->getGroups($model, $parentGroups), $model->options));
$annotation->ref = $this->modelRegistry->register(new Model($this->createType($model->type), $this->getGroups($model, $parentGroups), $model->options, $model->serializationContext));

// It is no longer an unmerged annotation
$this->detach($model, $annotation, $analysis);
Expand All @@ -71,7 +71,7 @@ public function __invoke(Analysis $analysis, array $parentGroups = null)
if ($annotation instanceof OA\Response || $annotation instanceof OA\RequestBody) {
$properties = [
'_context' => Util::createContext(['nested' => $annotation], $annotation->_context),
'ref' => $this->modelRegistry->register(new Model($this->createType($model->type), $this->getGroups($model, $parentGroups), $model->options)),
'ref' => $this->modelRegistry->register(new Model($this->createType($model->type), $this->getGroups($model, $parentGroups), $model->options, $model->serializationContext)),
];

foreach ($this->mediaTypes as $mediaType) {
Expand All @@ -98,7 +98,7 @@ public function __invoke(Analysis $analysis, array $parentGroups = null)
}

$annotation->merge([new $annotationClass([
'ref' => $this->modelRegistry->register(new Model($this->createType($model->type), $this->getGroups($model, $parentGroups), $model->options)),
'ref' => $this->modelRegistry->register(new Model($this->createType($model->type), $this->getGroups($model, $parentGroups), $model->options, $model->serializationContext)),
])]);

// It is no longer an unmerged annotation
Expand Down
15 changes: 15 additions & 0 deletions Tests/Functional/Controller/ApiController80.php
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
use Nelmio\ApiDocBundle\Tests\Functional\Entity\Article;
use Nelmio\ApiDocBundle\Tests\Functional\Entity\ArticleInterface;
use Nelmio\ApiDocBundle\Tests\Functional\Entity\CompoundEntity;
use Nelmio\ApiDocBundle\Tests\Functional\Entity\EntityThroughNameConverter;
use Nelmio\ApiDocBundle\Tests\Functional\Entity\EntityWithAlternateType80;
use Nelmio\ApiDocBundle\Tests\Functional\Entity\EntityWithNullableSchemaSet;
use Nelmio\ApiDocBundle\Tests\Functional\Entity\EntityWithObjectType;
Expand Down Expand Up @@ -468,4 +469,18 @@ public function entityWithNullableSchemaSet()
public function serializedNameAction()
{
}

/**
* @Route("/name_converter_context", methods={"GET"})
*
* @OA\Response(
* response="200",
* description="",
*
* @Model(type=EntityThroughNameConverter::class, serializationContext={"secret_name_converter_value"=true})
* )
*/
public function nameConverterContext()
{
}
}
7 changes: 7 additions & 0 deletions Tests/Functional/Controller/ApiController81.php
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
use Nelmio\ApiDocBundle\Tests\Functional\Entity\Article81;
use Nelmio\ApiDocBundle\Tests\Functional\Entity\ArticleInterface;
use Nelmio\ApiDocBundle\Tests\Functional\Entity\CompoundEntity;
use Nelmio\ApiDocBundle\Tests\Functional\Entity\EntityThroughNameConverter;
use Nelmio\ApiDocBundle\Tests\Functional\Entity\EntityWithAlternateType81;
use Nelmio\ApiDocBundle\Tests\Functional\Entity\EntityWithNullableSchemaSet;
use Nelmio\ApiDocBundle\Tests\Functional\Entity\EntityWithObjectType;
Expand Down Expand Up @@ -423,4 +424,10 @@ public function enum()
public function serializedNameAction()
{
}

#[Route('/name_converter_context', methods: ['GET'])]
#[OA\Response(response: '200', description: '', content: new Model(type: EntityThroughNameConverter::class, serializationContext: ['secret_name_converter_value' => true]))]
public function nameConverterContext()
{
}
}
16 changes: 16 additions & 0 deletions Tests/Functional/Entity/EntityThroughNameConverter.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
<?php

namespace Nelmio\ApiDocBundle\Tests\Functional\Entity;

class EntityThroughNameConverter
{
/**
* @var int
*/
public $id;

/**
* @var string
*/
public $name;
}
12 changes: 12 additions & 0 deletions Tests/Functional/FunctionalTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -762,4 +762,16 @@ public function testEntityWithNullableSchemaSet()
// nonNullablePropertyNullableTrueSet
$this->assertTrue($model->properties[5]->nullable);
}

public function testContextPassedToNameConverter()
{
$this->getOperation('/api/name_converter_context', 'get');

$model = $this->getModel('EntityThroughNameConverter');
$this->assertCount(2, $model->properties);
$this->assertNotHasProperty('id', $model);
$this->assertHasProperty('name_converter_context_id', $model);
$this->assertNotHasProperty('name', $model);
$this->assertHasProperty('name_converter_context_name', $model);
}
}
35 changes: 35 additions & 0 deletions Tests/Functional/ModelDescriber/NameConverter.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
<?php

declare(strict_types=1);

namespace Nelmio\ApiDocBundle\Tests\Functional\ModelDescriber;

use Symfony\Component\Serializer\NameConverter\AdvancedNameConverterInterface;
use Symfony\Component\Serializer\NameConverter\MetadataAwareNameConverter;

class NameConverter implements AdvancedNameConverterInterface
{
/**
* @var MetadataAwareNameConverter
*/
private $inner;

public function __construct(MetadataAwareNameConverter $inner)
{
$this->inner = $inner;
}

public function normalize(string $propertyName, string $class = null, string $format = null, array $context = []): string
{
if (!isset($context['secret_name_converter_value'])) {
return $this->inner->normalize($propertyName, $class, $format, $context);
}

return 'name_converter_context_'.$propertyName;
}

public function denormalize(string $propertyName, string $class = null, string $format = null, array $context = []): string
{
throw new \RuntimeException('Was not expected to be called');
}
}
2 changes: 1 addition & 1 deletion Tests/Functional/Resources/routes.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -37,4 +37,4 @@ doc_json:

doc_yaml:
path: /{area}/docs.yaml
controller: nelmio_api_doc.controller.swagger_yaml
controller: nelmio_api_doc.controller.swagger_yaml
6 changes: 6 additions & 0 deletions Tests/Functional/TestKernel.php
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
use Nelmio\ApiDocBundle\Tests\Functional\Entity\NestedGroup\JMSPicture;
use Nelmio\ApiDocBundle\Tests\Functional\Entity\PrivateProtectedExposure;
use Nelmio\ApiDocBundle\Tests\Functional\Entity\SymfonyConstraintsWithValidationGroups;
use Nelmio\ApiDocBundle\Tests\Functional\ModelDescriber\NameConverter;
use Nelmio\ApiDocBundle\Tests\Functional\ModelDescriber\VirtualTypeClassDoesNotExistsHandlerDefinedDescriber;
use ReflectionException;
use ReflectionMethod;
Expand All @@ -35,6 +36,7 @@
use Symfony\Component\Config\Loader\LoaderInterface;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\Definition;
use Symfony\Component\DependencyInjection\Reference;
use Symfony\Component\HttpKernel\Kernel;
use Symfony\Component\Routing\Loader\Configurator\RoutingConfigurator;

Expand Down Expand Up @@ -349,6 +351,10 @@ protected function configureContainer(ContainerBuilder $c, LoaderInterface $load
$def = new Definition(VirtualTypeClassDoesNotExistsHandlerDefinedDescriber::class);
$def->addTag('nelmio_api_doc.model_describer');
$c->setDefinition('nelmio.test.jms.virtual_type.describer', $def);

$c->register('serializer.name_converter.custom', NameConverter::class)
->setDecoratedService('serializer.name_converter.metadata_aware')
->addArgument(new Reference('serializer.name_converter.custom.inner'));
}

public function getCacheDir(): string
Expand Down
15 changes: 15 additions & 0 deletions phpunit-baseline.json
Original file line number Diff line number Diff line change
Expand Up @@ -6903,5 +6903,20 @@
"location": "Nelmio\\ApiDocBundle\\Tests\\Functional\\ValidationGroupsFunctionalTest::testConstraintDefaultGroupsAreRespectedWhenReadingAnnotations",
"message": "Since sensio/framework-extra-bundle 5.2: The \"sensio_framework_extra.routing.loader.annot_file\" service is deprecated since version 5.2",
"count": 1
},
{
"location": "Nelmio\\ApiDocBundle\\Tests\\Functional\\FunctionalTest::testContextPassedToNameConverter",
"message": "Since sensio/framework-extra-bundle 5.2: The \"sensio_framework_extra.routing.loader.annot_class\" service is deprecated since version 5.2",
"count": 1
},
{
"location": "Nelmio\\ApiDocBundle\\Tests\\Functional\\FunctionalTest::testContextPassedToNameConverter",
"message": "Since sensio/framework-extra-bundle 5.2: The \"sensio_framework_extra.routing.loader.annot_dir\" service is deprecated since version 5.2",
"count": 1
},
{
"location": "Nelmio\\ApiDocBundle\\Tests\\Functional\\FunctionalTest::testContextPassedToNameConverter",
"message": "Since sensio/framework-extra-bundle 5.2: The \"sensio_framework_extra.routing.loader.annot_file\" service is deprecated since version 5.2",
"count": 1
}
]

0 comments on commit c55d9ef

Please sign in to comment.