Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix #4005 BC layer for openapi normalizer #4016

Merged
merged 10 commits into from
Feb 3, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
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));
}
}