Skip to content

Commit

Permalink
Fix #4005 BC layer for openapi normalizer (#4016)
Browse files Browse the repository at this point in the history
* Fix #4005 BC layer for openapi normalizer

* whoops

* whoops

* whoops

* whoops

* Review

* Review

* we have the bc layer covered

* alright now

* improve message
  • Loading branch information
soyuka committed Feb 3, 2021
1 parent d008a59 commit c46f6fb
Show file tree
Hide file tree
Showing 8 changed files with 83 additions and 5 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -392,6 +392,10 @@ private function registerSwaggerConfiguration(ContainerBuilder $container, array
$container->setParameter('api_platform.enable_swagger_ui', $config['enable_swagger_ui']);
$container->setParameter('api_platform.enable_re_doc', $config['enable_re_doc']);
$container->setParameter('api_platform.swagger.api_keys', $config['swagger']['api_keys']);

if (true === $config['openapi']['backward_compatibility_layer']) {
$container->getDefinition('api_platform.swagger.normalizer.documentation')->addArgument($container->getDefinition('api_platform.openapi.normalizer'));
}
}

private function registerJsonApiConfiguration(array $formats, XmlFileLoader $loader): void
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -483,6 +483,7 @@ private function addOpenApiSection(ArrayNodeDefinition $rootNode): void
->scalarNode('email')->defaultNull()->info('The email address of the contact person/organization. MUST be in the format of an email address.')->end()
->end()
->end()
->booleanNode('backward_compatibility_layer')->defaultTrue()->info('Enable this to decorate the "api_platform.swagger.normalizer.documentation" instead of decorating the OpenAPI factory.')->end()
->scalarNode('termsOfService')->defaultNull()->info('A URL to the Terms of Service for the API. MUST be in the format of a URL.')->end()
->arrayNode('license')
->addDefaultsIfNotSet()
Expand Down
3 changes: 2 additions & 1 deletion src/Bridge/Symfony/Bundle/Resources/config/openapi.xml
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,8 @@
<services>
<service id="api_platform.openapi.normalizer" class="ApiPlatform\Core\OpenApi\Serializer\OpenApiNormalizer" public="false">
<argument type="service" id="serializer.normalizer.object" />
<tag name="serializer.normalizer" priority="-785" />
<!-- Just after the DocumentationNormalizer see swagger.xml -->
<tag name="serializer.normalizer" priority="-795" />
</service>
<service id="ApiPlatform\Core\OpenApi\Serializer\OpenApiNormalizer" alias="api_platform.openapi.normalizer" />

Expand Down
8 changes: 6 additions & 2 deletions src/OpenApi/Serializer/OpenApiNormalizer.php
Original file line number Diff line number Diff line change
Expand Up @@ -55,13 +55,17 @@ private function recursiveClean($data): array
}

if ('schemas' === $key) {
ksort($value);
if ($value) {
ksort($value);
}
}

// Side effect of using getPaths(): Paths which itself contains the array
if ('paths' === $key) {
$value = $data['paths'] = $data['paths']['paths'];
ksort($value);
if ($value) {
ksort($value);
}
unset($data['paths']['paths']);
}

Expand Down
14 changes: 12 additions & 2 deletions src/Swagger/Serializer/DocumentationNormalizer.php
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@
use ApiPlatform\Core\Metadata\Property\Factory\PropertyNameCollectionFactoryInterface;
use ApiPlatform\Core\Metadata\Resource\Factory\ResourceMetadataFactoryInterface;
use ApiPlatform\Core\Metadata\Resource\ResourceMetadata;
use ApiPlatform\Core\OpenApi\OpenApi;
use ApiPlatform\Core\Operation\Factory\SubresourceOperationFactoryInterface;
use ApiPlatform\Core\PathResolver\OperationPathResolverInterface;
use Psr\Container\ContainerInterface;
Expand Down Expand Up @@ -102,14 +103,16 @@ final class DocumentationNormalizer implements NormalizerInterface, CacheableSup

private $identifiersExtractor;

private $openApiNormalizer;

/**
* @param SchemaFactoryInterface|ResourceClassResolverInterface|null $jsonSchemaFactory
* @param ContainerInterface|FilterCollection|null $filterLocator
* @param array|OperationAwareFormatsProviderInterface $formats
* @param mixed|null $jsonSchemaTypeFactory
* @param int[] $swaggerVersions
*/
public function __construct(ResourceMetadataFactoryInterface $resourceMetadataFactory, PropertyNameCollectionFactoryInterface $propertyNameCollectionFactory, PropertyMetadataFactoryInterface $propertyMetadataFactory, $jsonSchemaFactory = null, $jsonSchemaTypeFactory = null, OperationPathResolverInterface $operationPathResolver = null, UrlGeneratorInterface $urlGenerator = null, $filterLocator = null, NameConverterInterface $nameConverter = null, bool $oauthEnabled = false, string $oauthType = '', string $oauthFlow = '', string $oauthTokenUrl = '', string $oauthAuthorizationUrl = '', array $oauthScopes = [], array $apiKeys = [], SubresourceOperationFactoryInterface $subresourceOperationFactory = null, bool $paginationEnabled = true, string $paginationPageParameterName = 'page', bool $clientItemsPerPage = false, string $itemsPerPageParameterName = 'itemsPerPage', $formats = [], bool $paginationClientEnabled = false, string $paginationClientEnabledParameterName = 'pagination', array $defaultContext = [], array $swaggerVersions = [2, 3], IdentifiersExtractorInterface $identifiersExtractor = null)
public function __construct(ResourceMetadataFactoryInterface $resourceMetadataFactory, PropertyNameCollectionFactoryInterface $propertyNameCollectionFactory, PropertyMetadataFactoryInterface $propertyMetadataFactory, $jsonSchemaFactory = null, $jsonSchemaTypeFactory = null, OperationPathResolverInterface $operationPathResolver = null, UrlGeneratorInterface $urlGenerator = null, $filterLocator = null, NameConverterInterface $nameConverter = null, bool $oauthEnabled = false, string $oauthType = '', string $oauthFlow = '', string $oauthTokenUrl = '', string $oauthAuthorizationUrl = '', array $oauthScopes = [], array $apiKeys = [], SubresourceOperationFactoryInterface $subresourceOperationFactory = null, bool $paginationEnabled = true, string $paginationPageParameterName = 'page', bool $clientItemsPerPage = false, string $itemsPerPageParameterName = 'itemsPerPage', $formats = [], bool $paginationClientEnabled = false, string $paginationClientEnabledParameterName = 'pagination', array $defaultContext = [], array $swaggerVersions = [2, 3], IdentifiersExtractorInterface $identifiersExtractor = null, NormalizerInterface $openApiNormalizer = null)
{
if ($jsonSchemaTypeFactory instanceof OperationMethodResolverInterface) {
@trigger_error(sprintf('Passing an instance of %s to %s() is deprecated since version 2.5 and will be removed in 3.0.', OperationMethodResolverInterface::class, __METHOD__), \E_USER_DEPRECATED);
Expand Down Expand Up @@ -171,13 +174,20 @@ public function __construct(ResourceMetadataFactoryInterface $resourceMetadataFa

$this->defaultContext = array_merge($this->defaultContext, $defaultContext);
$this->identifiersExtractor = $identifiersExtractor;
$this->openApiNormalizer = $openApiNormalizer;
}

/**
* {@inheritdoc}
*/
public function normalize($object, $format = null, array $context = [])
{
if ($object instanceof OpenApi) {
@trigger_error('Using the swagger DocumentationNormalizer is deprecated in favor of decorating the OpenApiFactory, use the "openapi.backward_compatibility_layer" configuration to change this behavior.', \E_USER_DEPRECATED);

return $this->openApiNormalizer->normalize($object, $format, $context);
}

$v3 = 3 === ($context['spec_version'] ?? $this->defaultContext['spec_version']) && !($context['api_gateway'] ?? $this->defaultContext['api_gateway']);

$definitions = new \ArrayObject();
Expand Down Expand Up @@ -779,7 +789,7 @@ private function getFiltersParameters(bool $v3, string $resourceClass, string $o
*/
public function supportsNormalization($data, $format = null): bool
{
return self::FORMAT === $format && $data instanceof Documentation;
return self::FORMAT === $format && ($data instanceof Documentation || $this->openApiNormalizer && $data instanceof OpenApi);
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -218,6 +218,7 @@ private function runDefaultConfigTests(array $doctrineIntegrationsToLoad = ['orm
'name' => null,
'url' => null,
],
'backward_compatibility_layer' => true,
],
], $config);
}
Expand Down
2 changes: 2 additions & 0 deletions tests/Fixtures/app/config/config_common.yml
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,8 @@ api_platform:
http_cache:
invalidation:
enabled: true
openapi:
backward_compatibility_layer: false
defaults:
pagination_client_enabled: true
pagination_client_items_per_page: true
Expand Down
55 changes: 55 additions & 0 deletions tests/Swagger/Serializer/DocumentationNormalizerV3Test.php
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
use ApiPlatform\Core\Api\OperationAwareFormatsProviderInterface;
use ApiPlatform\Core\Api\OperationMethodResolverInterface;
use ApiPlatform\Core\Api\OperationType;
use ApiPlatform\Core\Api\ResourceClassResolverInterface;
use ApiPlatform\Core\Bridge\Symfony\Routing\RouterOperationPathResolver;
use ApiPlatform\Core\Documentation\Documentation;
use ApiPlatform\Core\Exception\InvalidArgumentException;
Expand All @@ -33,6 +34,8 @@
use ApiPlatform\Core\Metadata\Resource\Factory\ResourceMetadataFactoryInterface;
use ApiPlatform\Core\Metadata\Resource\ResourceMetadata;
use ApiPlatform\Core\Metadata\Resource\ResourceNameCollection;
use ApiPlatform\Core\OpenApi\Model;
use ApiPlatform\Core\OpenApi\OpenApi;
use ApiPlatform\Core\Operation\Factory\SubresourceOperationFactory;
use ApiPlatform\Core\Operation\UnderscorePathSegmentNameGenerator;
use ApiPlatform\Core\PathResolver\CustomOperationPathResolver;
Expand All @@ -54,6 +57,7 @@
use Symfony\Component\Routing\RouterInterface;
use Symfony\Component\Serializer\NameConverter\NameConverterInterface;
use Symfony\Component\Serializer\Normalizer\AbstractNormalizer;
use Symfony\Component\Serializer\Normalizer\NormalizerInterface;

/**
* @author Amrouche Hamza <hamza.simperfit@gmail.com>
Expand Down Expand Up @@ -3208,4 +3212,55 @@ private function doTestNormalizeWithCustomFormatsDefinedAtOperationLevel(Operati

$this->assertEquals($expected, $normalizer->normalize($documentation, DocumentationNormalizer::FORMAT, ['base_url' => '/']));
}

/**
* @group legacy
* @expectedDeprecation Using the swagger DocumentationNormalizer is deprecated in favor of decorating the OpenApiFactory, use the "openapi.backward_compatibility_layer" configuration to change this behavior.
*/
public function testNormalizeOpenApi()
{
$openapi = new OpenApi(new Model\Info('api', 'v1'), [], new Model\Paths());
$propertyNameCollectionFactoryProphecy = $this->prophesize(PropertyNameCollectionFactoryInterface::class);
$resourceMetadataFactoryProphecy = $this->prophesize(ResourceMetadataFactoryInterface::class);
$propertyMetadataFactoryProphecy = $this->prophesize(PropertyMetadataFactoryInterface::class);
$resourceClassResolverProphecy = $this->prophesize(ResourceClassResolverInterface::class);
$operationPathResolver = new OperationPathResolver(new UnderscorePathSegmentNameGenerator());
$identifiersExtractorProphecy = $this->prophesize(IdentifiersExtractorInterface::class);

$openApiNormalizerProphecy = $this->prophesize(NormalizerInterface::class);
$openApiNormalizerProphecy->normalize($openapi, null, [])->willReturn([])->shouldBeCalled();

$normalizer = new DocumentationNormalizer(
$resourceMetadataFactoryProphecy->reveal(),
$propertyNameCollectionFactoryProphecy->reveal(),
$propertyMetadataFactoryProphecy->reveal(),
null,
null,
$operationPathResolver,
null,
null,
null, false,
'',
'',
'',
'',
[],
[],
null,
false,
'page',
false,
'itemsPerPage',
$formatsProvider ?? [],
false,
'pagination',
['spec_version' => 3],
[2, 3],
$identifiersExtractorProphecy->reveal(),
$openApiNormalizerProphecy->reveal()
);

$this->assertTrue($normalizer->supportsNormalization($openapi, 'json'));
$this->assertEquals([], $normalizer->normalize($openapi));
}
}

0 comments on commit c46f6fb

Please sign in to comment.