log name collisions (#1862)

This commit is contained in:
Asmir Mustafic 2021-08-17 21:51:11 +02:00 committed by GitHub
parent 6ebb9cc9b4
commit 985b43fb60
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 134 additions and 2 deletions

View File

@ -20,9 +20,12 @@ use Nelmio\ApiDocBundle\OpenApiPhp\ModelRegister;
use OpenApi\Analysis;
use OpenApi\Annotations\OpenApi;
use Psr\Cache\CacheItemPoolInterface;
use Psr\Log\LoggerAwareTrait;
final class ApiDocGenerator
{
use LoggerAwareTrait;
/** @var OpenApi */
private $openApi;
@ -81,6 +84,9 @@ final class ApiDocGenerator
$this->openApi = new OpenApi([]);
$modelRegistry = new ModelRegistry($this->modelDescribers, $this->openApi, $this->alternativeNames);
if (null !== $this->logger) {
$modelRegistry->setLogger($this->logger);
}
foreach ($this->describers as $describer) {
if ($describer instanceof ModelRegistryAwareInterface) {
$describer->setModelRegistry($modelRegistry);

View File

@ -66,11 +66,12 @@ final class NelmioApiDocExtension extends Extension implements PrependExtensionI
$container->setParameter('nelmio_api_doc.media_types', $config['media_types']);
foreach ($config['areas'] as $area => $areaConfig) {
$nameAliases = $this->findNameAliases($config['models']['names'], $area);
$container->register(sprintf('nelmio_api_doc.generator.%s', $area), ApiDocGenerator::class)
->setPublic(true)
->addMethodCall('setAlternativeNames', [$nameAliases])
->addMethodCall('setMediaTypes', [$config['media_types']])
->addMethodCall('setLogger', [new Reference('logger')])
->addTag('monolog.logger', ['channel' => 'nelmio_api_doc'])
->setArguments([
new TaggedIteratorArgument(sprintf('nelmio_api_doc.describer.%s', $area)),
new TaggedIteratorArgument('nelmio_api_doc.model_describer'),

View File

@ -15,10 +15,16 @@ use Nelmio\ApiDocBundle\Describer\ModelRegistryAwareInterface;
use Nelmio\ApiDocBundle\ModelDescriber\ModelDescriberInterface;
use Nelmio\ApiDocBundle\OpenApiPhp\Util;
use OpenApi\Annotations as OA;
use Psr\Log\LoggerAwareTrait;
use Psr\Log\NullLogger;
use Symfony\Component\PropertyInfo\Type;
final class ModelRegistry
{
use LoggerAwareTrait;
private $registeredModelNames = [];
private $alternativeNames = [];
private $unregistered = [];
@ -40,10 +46,11 @@ final class ModelRegistry
{
$this->modelDescribers = $modelDescribers;
$this->api = $api;
$this->logger = new NullLogger();
foreach (array_reverse($alternativeNames) as $alternativeName => $criteria) {
$this->alternativeNames[] = $model = new Model(new Type('object', false, $criteria['type']), $criteria['groups']);
$this->names[$model->getHash()] = $alternativeName;
$this->registeredModelNames[$alternativeName] = $model;
Util::getSchema($this->api, $alternativeName);
}
}
@ -57,6 +64,7 @@ final class ModelRegistry
}
if (!isset($this->names[$hash])) {
$this->names[$hash] = $this->generateModelName($model);
$this->registeredModelNames[$this->names[$hash]] = $model;
}
// Reserve the name
@ -115,6 +123,12 @@ final class ModelRegistry
);
$i = 1;
while (\in_array($name, $names, true)) {
if (isset($this->registeredModelNames[$name])) {
$this->logger->info(sprintf('Can not assign a name for the model, the name "%s" has already been taken.', $name), [
'model' => $this->modelToArray($model),
'taken_by' => $this->modelToArray($this->registeredModelNames[$name]),
]);
}
++$i;
$name = $base.$i;
}
@ -122,6 +136,26 @@ final class ModelRegistry
return $name;
}
private function modelToArray(Model $model): array
{
$getType = function (Type $type) use (&$getType) {
return [
'class' => $type->getClassName(),
'built_in_type' => $type->getBuiltinType(),
'nullable' => $type->isNullable(),
'collection' => $type->isCollection(),
'collection_key_types' => $type->isCollection() ? array_map($getType, $type->getCollectionKeyTypes()) : null,
'collection_value_types' => $type->isCollection() ? array_map($getType, $type->getCollectionValueTypes()) : null,
];
};
return [
'type' => $getType($model->getType()),
'options' => $model->getOptions(),
'groups' => $model->getGroups(),
];
}
private function getTypeShortName(Type $type): string
{
if (null !== $type->getCollectionValueType()) {

View File

@ -15,6 +15,7 @@ use Nelmio\ApiDocBundle\Model\Model;
use Nelmio\ApiDocBundle\Model\ModelRegistry;
use OpenApi\Annotations as OA;
use PHPUnit\Framework\TestCase;
use Psr\Log\LoggerInterface;
use Symfony\Component\PropertyInfo\Type;
class ModelRegistryTest extends TestCase
@ -33,6 +34,96 @@ class ModelRegistryTest extends TestCase
$this->assertEquals('#/components/schemas/array', $registry->register(new Model($type, ['group1'])));
}
public function testNameCollisionsAreLogged()
{
$logger = $this->createMock(LoggerInterface::class);
$logger
->expects(self::once())
->method('info')
->with(
'Can not assign a name for the model, the name "ModelRegistryTest" has already been taken.', [
'model' => [
'type' => [
'class' => 'Nelmio\\ApiDocBundle\\Tests\\Model\\ModelRegistryTest',
'built_in_type' => 'object',
'nullable' => false,
'collection' => false,
'collection_key_types' => null,
'collection_value_types' => null,
],
'options' => null,
'groups' => ['group2'],
],
'taken_by' => [
'type' => [
'class' => 'Nelmio\\ApiDocBundle\\Tests\\Model\\ModelRegistryTest',
'built_in_type' => 'object',
'nullable' => false,
'collection' => false,
'collection_key_types' => null,
'collection_value_types' => null,
],
'options' => null,
'groups' => ['group1'],
],
]);
$registry = new ModelRegistry([], new OA\OpenApi([]), []);
$registry->setLogger($logger);
$type = new Type(Type::BUILTIN_TYPE_OBJECT, false, self::class);
$registry->register(new Model($type, ['group1']));
$registry->register(new Model($type, ['group2']));
}
public function testNameCollisionsAreLoggedWithAlternativeNames()
{
$ref = new \ReflectionClass(self::class);
$alternativeNames = [
$ref->getShortName() => [
'type' => $ref->getName(),
'groups' => ['group1'],
],
];
$logger = $this->createMock(LoggerInterface::class);
$logger
->expects(self::once())
->method('info')
->with(
'Can not assign a name for the model, the name "ModelRegistryTest" has already been taken.', [
'model' => [
'type' => [
'class' => 'Nelmio\\ApiDocBundle\\Tests\\Model\\ModelRegistryTest',
'built_in_type' => 'object',
'nullable' => false,
'collection' => false,
'collection_key_types' => null,
'collection_value_types' => null,
],
'options' => null,
'groups' => ['group2'],
],
'taken_by' => [
'type' => [
'class' => 'Nelmio\\ApiDocBundle\\Tests\\Model\\ModelRegistryTest',
'built_in_type' => 'object',
'nullable' => false,
'collection' => false,
'collection_key_types' => null,
'collection_value_types' => null,
],
'options' => null,
'groups' => ['group1'],
],
]);
$registry = new ModelRegistry([], new OA\OpenApi([]), $alternativeNames);
$registry->setLogger($logger);
$type = new Type(Type::BUILTIN_TYPE_OBJECT, false, self::class);
$registry->register(new Model($type, ['group2']));
}
/**
* @dataProvider getNameAlternatives
*