Skip to content

Commit

Permalink
Merge 3.3
Browse files Browse the repository at this point in the history
  • Loading branch information
soyuka committed May 29, 2024
2 parents 40fd600 + b5a93fb commit 9c3faed
Show file tree
Hide file tree
Showing 16 changed files with 141 additions and 21 deletions.
2 changes: 1 addition & 1 deletion .commitlintrc
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,7 @@
"build",
"chore",
"ci",
"docs",
"doc",
"feat",
"fix",
"perf",
Expand Down
8 changes: 7 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,11 @@
# Changelog

## v3.3.5

### Bug fixes

* [55f27dc7a](https://github.com/api-platform/core/commit/55f27dc7adbf2456ec7700dd57e10c036eb260c0) fix(symfony): documentation request _format (#6390)

## v3.3.4

### Bug fixes
Expand Down Expand Up @@ -2310,4 +2316,4 @@ Please read #2825 if you have issues with the behavior of Readable/Writable Link
## 1.0.0 beta 2

* Preserve indexes when normalizing and denormalizing associative arrays
* Allow setting default order for property when registering a `Doctrine\Orm\Filter\OrderFilter` instance
* Allow setting default order for property when registering a `Doctrine\Orm\Filter\OrderFilter` instance
6 changes: 6 additions & 0 deletions features/openapi/docs.feature
Original file line number Diff line number Diff line change
Expand Up @@ -432,3 +432,9 @@ Feature: Documentation support
]
"""
And the JSON node "components.schemas.DummyBoolean.properties.isDummyBoolean.owl:maxCardinality" should not exist

Scenario: Retrieve the OpenAPI documentation in JSON
Given I add "Accept" header equal to "text/html,*/*;q=0.8"
And I send a "GET" request to "/docs.jsonopenapi"
Then the response status code should be 200
And the response should be in JSON
16 changes: 15 additions & 1 deletion src/Metadata/Util/ContentNegotiationTrait.php
Original file line number Diff line number Diff line change
Expand Up @@ -106,7 +106,7 @@ private function getRequestFormat(Request $request, array $formats, bool $throw
}
}

// Then use the Symfony request format if available and applicable
// Then, use the Symfony request format if available and applicable
$requestFormat = $request->getRequestFormat('') ?: null;
if (null !== $requestFormat) {
$mimeType = $request->getMimeType($requestFormat);
Expand Down Expand Up @@ -135,4 +135,18 @@ private function getNotAcceptableHttpException(string $accept, array $mimeTypes)
implode('", "', array_keys($mimeTypes))
));
}

/**
* Adds the supported formats to the request.
*
* This is necessary for {@see Request::getMimeType} and {@see Request::getMimeTypes} to work.
*
* @param array<string, string|string[]> $formats
*/
private function addRequestFormats(Request $request, array $formats): void
{
foreach ($formats as $format => $mimeTypes) {
$request->setFormat($format, (array) $mimeTypes);
}
}
}
38 changes: 38 additions & 0 deletions src/OpenApi/Serializer/SerializerContextBuilder.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
<?php

/*
* This file is part of the API Platform project.
*
* (c) Kévin Dunglas <dunglas@gmail.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/

declare(strict_types=1);

namespace ApiPlatform\OpenApi\Serializer;

use ApiPlatform\State\SerializerContextBuilderInterface;
use Symfony\Component\HttpFoundation\Request;

/**
* @internal
*/
final class SerializerContextBuilder implements SerializerContextBuilderInterface
{
public function __construct(private readonly SerializerContextBuilderInterface $decorated)
{
}

public function createFromRequest(Request $request, bool $normalization, ?array $extractedAttributes = null): array
{
$context = $this->decorated->createFromRequest($request, $normalization, $extractedAttributes);

return $context + [
'api_gateway' => $request->query->getBoolean(ApiGatewayNormalizer::API_GATEWAY),
'base_url' => $request->getBaseUrl(),
'spec_version' => (string) $request->query->get(LegacyOpenApiNormalizer::SPEC_VERSION),
];
}
}
34 changes: 34 additions & 0 deletions src/OpenApi/State/OpenApiProvider.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
<?php

/*
* This file is part of the API Platform project.
*
* (c) Kévin Dunglas <dunglas@gmail.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/

declare(strict_types=1);

namespace ApiPlatform\OpenApi\State;

use ApiPlatform\Metadata\Operation;
use ApiPlatform\OpenApi\Factory\OpenApiFactoryInterface;
use ApiPlatform\OpenApi\OpenApi;
use ApiPlatform\State\ProviderInterface;

/**
* @internal
*/
final class OpenApiProvider implements ProviderInterface
{
public function __construct(private readonly OpenApiFactoryInterface $openApiFactory)
{
}

public function provide(Operation $operation, array $uriVariables = [], array $context = []): OpenApi
{
return $this->openApiFactory->__invoke($context);
}
}
11 changes: 8 additions & 3 deletions src/State/Provider/DeserializeProvider.php
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,9 @@

use ApiPlatform\Metadata\HttpOperation;
use ApiPlatform\Metadata\Operation;
use ApiPlatform\Serializer\SerializerContextBuilderInterface;
use ApiPlatform\Serializer\SerializerContextBuilderInterface as LegacySerializerContextBuilderInterface;
use ApiPlatform\State\ProviderInterface;
use ApiPlatform\State\SerializerContextBuilderInterface;
use ApiPlatform\Validator\Exception\ValidationException;
use Symfony\Component\HttpKernel\Exception\UnsupportedMediaTypeHttpException;
use Symfony\Component\Serializer\Exception\NotNormalizableValueException;
Expand All @@ -32,8 +33,12 @@

final class DeserializeProvider implements ProviderInterface
{
public function __construct(private readonly ?ProviderInterface $decorated, private readonly SerializerInterface $serializer, private readonly SerializerContextBuilderInterface $serializerContextBuilder, private ?TranslatorInterface $translator = null)
{
public function __construct(
private readonly ?ProviderInterface $decorated,
private readonly SerializerInterface $serializer,
private readonly LegacySerializerContextBuilderInterface|SerializerContextBuilderInterface $serializerContextBuilder,
private ?TranslatorInterface $translator = null
) {
if (null === $this->translator) {
$this->translator = new class() implements TranslatorInterface, LocaleAwareInterface {
use TranslatorTrait;
Expand Down
5 changes: 3 additions & 2 deletions src/State/Provider/ReadProvider.php
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,10 @@
use ApiPlatform\Metadata\Operation;
use ApiPlatform\Metadata\Put;
use ApiPlatform\Metadata\Util\CloneTrait;
use ApiPlatform\Serializer\SerializerContextBuilderInterface;
use ApiPlatform\Serializer\SerializerContextBuilderInterface as LegacySerializerContextBuilderInterface;
use ApiPlatform\State\Exception\ProviderNotFoundException;
use ApiPlatform\State\ProviderInterface;
use ApiPlatform\State\SerializerContextBuilderInterface;
use ApiPlatform\State\UriVariablesResolverTrait;
use ApiPlatform\State\Util\OperationRequestInitiatorTrait;
use ApiPlatform\State\Util\RequestParser;
Expand All @@ -38,7 +39,7 @@ final class ReadProvider implements ProviderInterface

public function __construct(
private readonly ProviderInterface $provider,
private readonly ?SerializerContextBuilderInterface $serializerContextBuilder = null,
private readonly LegacySerializerContextBuilderInterface|SerializerContextBuilderInterface|null $serializerContextBuilder = null,
) {
}

Expand Down
2 changes: 1 addition & 1 deletion src/State/Tests/Provider/ReadProviderTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,9 @@
namespace ApiPlatform\State\Tests\Provider;

use ApiPlatform\Metadata\Get;
use ApiPlatform\Serializer\SerializerContextBuilderInterface;
use ApiPlatform\State\Provider\ReadProvider;
use ApiPlatform\State\ProviderInterface;
use ApiPlatform\State\SerializerContextBuilderInterface;
use PHPUnit\Framework\TestCase;
use Symfony\Component\HttpFoundation\Request;

Expand Down
7 changes: 2 additions & 5 deletions src/Symfony/Action/DocumentationAction.php
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,7 @@ public function __invoke(?Request $request = null)
'spec_version' => (string) $request->query->get(LegacyOpenApiNormalizer::SPEC_VERSION),
];
$request->attributes->set('_api_normalization_context', $request->attributes->get('_api_normalization_context', []) + $context);
$this->addRequestFormats($request, $this->documentationFormats);
$format = $this->getRequestFormat($request, $this->documentationFormats);

if (null !== $this->openApiFactory && ('html' === $format || OpenApiNormalizer::FORMAT === $format || OpenApiNormalizer::JSON_FORMAT === $format || OpenApiNormalizer::YAML_FORMAT === $format)) {
Expand All @@ -87,11 +88,7 @@ private function getOpenApiDocumentation(array $context, string $format, Request
class: OpenApi::class,
read: true,
serialize: true,
provider: fn () => $this->openApiFactory->__invoke($context),
normalizationContext: [
ApiGatewayNormalizer::API_GATEWAY => $context['api_gateway'] ?? null,
LegacyOpenApiNormalizer::SPEC_VERSION => $context['spec_version'] ?? null,
],
provider: 'api_platform.openapi.provider',
outputFormats: $this->documentationFormats
);

Expand Down
11 changes: 11 additions & 0 deletions src/Symfony/Bundle/Resources/config/openapi.xml
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,17 @@
</service>
<service id="ApiPlatform\OpenApi\Serializer\OpenApiNormalizer" alias="api_platform.openapi.normalizer" />

<service id="api_platform.openapi.provider" class="ApiPlatform\OpenApi\State\OpenApiProvider" public="false">
<argument type="service" id="api_platform.openapi.factory" />

<tag name="api_platform.state_provider" priority="-100" key="ApiPlatform\OpenApi\State\OpenApiProvider" />
<tag name="api_platform.state_provider" priority="-100" key="api_platform.openapi.provider" />
</service>

<service id="api_platform.openapi.serializer_context_builder" class="ApiPlatform\OpenApi\Serializer\SerializerContextBuilder" decorates="api_platform.serializer.context_builder" public="false">
<argument type="service" id="api_platform.openapi.serializer_context_builder.inner" />
</service>

<service id="api_platform.openapi.options" class="ApiPlatform\OpenApi\Options">
<argument>%api_platform.title%</argument>
<argument>%api_platform.description%</argument>
Expand Down
11 changes: 8 additions & 3 deletions src/Symfony/EventListener/DeserializeListener.php
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,9 @@
use ApiPlatform\Api\FormatMatcher;
use ApiPlatform\Metadata\HttpOperation;
use ApiPlatform\Metadata\Resource\Factory\ResourceMetadataCollectionFactoryInterface;
use ApiPlatform\Serializer\SerializerContextBuilderInterface;
use ApiPlatform\Serializer\SerializerContextBuilderInterface as LegacySerializerContextBuilderInterface;
use ApiPlatform\State\ProviderInterface;
use ApiPlatform\State\SerializerContextBuilderInterface;
use ApiPlatform\State\Util\OperationRequestInitiatorTrait;
use ApiPlatform\Symfony\Util\RequestAttributesExtractor;
use ApiPlatform\Symfony\Validator\Exception\ValidationException;
Expand Down Expand Up @@ -48,8 +49,12 @@ final class DeserializeListener
private SerializerInterface $serializer;
private ?ProviderInterface $provider = null;

public function __construct(ProviderInterface|SerializerInterface $serializer, private readonly SerializerContextBuilderInterface|ResourceMetadataCollectionFactoryInterface|null $serializerContextBuilder = null, ?ResourceMetadataCollectionFactoryInterface $resourceMetadataFactory = null, private ?TranslatorInterface $translator = null)
{
public function __construct(
ProviderInterface|SerializerInterface $serializer,
private readonly LegacySerializerContextBuilderInterface|SerializerContextBuilderInterface|ResourceMetadataCollectionFactoryInterface|null $serializerContextBuilder = null,
?ResourceMetadataCollectionFactoryInterface $resourceMetadataFactory = null,
private ?TranslatorInterface $translator = null
) {
if ($serializer instanceof ProviderInterface) {
$this->provider = $serializer;
} else {
Expand Down
5 changes: 3 additions & 2 deletions src/Symfony/EventListener/ReadListener.php
Original file line number Diff line number Diff line change
Expand Up @@ -22,11 +22,12 @@
use ApiPlatform\Metadata\Resource\Factory\ResourceMetadataCollectionFactoryInterface;
use ApiPlatform\Metadata\UriVariablesConverterInterface;
use ApiPlatform\Metadata\Util\CloneTrait;
use ApiPlatform\Serializer\SerializerContextBuilderInterface;
use ApiPlatform\Serializer\SerializerContextBuilderInterface as LegacySerializerContextBuilderInterface;
use ApiPlatform\State\CallableProvider;
use ApiPlatform\State\Exception\ProviderNotFoundException;
use ApiPlatform\State\Provider\ReadProvider;
use ApiPlatform\State\ProviderInterface;
use ApiPlatform\State\SerializerContextBuilderInterface;
use ApiPlatform\State\UriVariablesResolverTrait;
use ApiPlatform\State\Util\OperationRequestInitiatorTrait;
use ApiPlatform\State\Util\RequestParser;
Expand All @@ -48,7 +49,7 @@ final class ReadListener
public function __construct(
private readonly ProviderInterface $provider,
?ResourceMetadataCollectionFactoryInterface $resourceMetadataCollectionFactory = null,
private readonly ?SerializerContextBuilderInterface $serializerContextBuilder = null,
private readonly LegacySerializerContextBuilderInterface|SerializerContextBuilderInterface|null $serializerContextBuilder = null,
LegacyUriVariablesConverterInterface|UriVariablesConverterInterface|null $uriVariablesConverter = null,
) {
$this->resourceMetadataCollectionFactory = $resourceMetadataCollectionFactory;
Expand Down
2 changes: 2 additions & 0 deletions tests/.ignored-deprecations
Original file line number Diff line number Diff line change
Expand Up @@ -12,3 +12,5 @@
%ApiPlatform\\Api\\FilterInterface is deprecated in favor of ApiPlatform\\Metadata\\FilterInterface%

%The "Symfony\\Bundle\\MakerBundle\\Maker\\MakeAuthenticator" class is deprecated, use any of the Security\\Make\* commands instead%

%Since symfony/validator 7.1: Not passing a value for the "requireTld" option to the Url constraint is deprecated. Its default value will change to "true".%
2 changes: 1 addition & 1 deletion tests/.ignored-deprecations-legacy-events
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@

%Since api-platform/core 3.3: Use a "ApiPlatform\\State\\ProviderInterface" as first argument in "ApiPlatform\\Symfony\\EventListener\\AddFormatListener" instead of "Negotiation\\Negotiator".%

%Since api-platform/core 3.3: Use a "ApiPlatform\\Metadata\\Resource\\Factory\\ResourceMetadataCollectionFactoryInterface" as second argument in "ApiPlatform\\Symfony\\EventListener\\DeserializeListener" instead of "ApiPlatform\\Serializer\\SerializerContextBuilderInterface".%
%Since api-platform/core 3.3: Use a "ApiPlatform\\Metadata\\Resource\\Factory\\ResourceMetadataCollectionFactoryInterface" as second argument in "ApiPlatform\\Symfony\\EventListener\\DeserializeListener" instead of "ApiPlatform\\State\\SerializerContextBuilderInterface".%

# Fixed when ApiPlatform\Api\FilterLocatorTrait will we deleted
%ApiPlatform\\Api\\FilterInterface is deprecated in favor of ApiPlatform\\Metadata\\FilterInterface%
Expand Down
2 changes: 1 addition & 1 deletion tests/State/Provider/DeserializeProviderTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,9 @@
namespace ApiPlatform\Tests\State\Provider;

use ApiPlatform\Metadata\Post;
use ApiPlatform\Serializer\SerializerContextBuilderInterface;
use ApiPlatform\State\Provider\DeserializeProvider;
use ApiPlatform\State\ProviderInterface;
use ApiPlatform\State\SerializerContextBuilderInterface;
use PHPUnit\Framework\TestCase;
use Prophecy\Argument;
use Prophecy\PhpUnit\ProphecyTrait;
Expand Down

0 comments on commit 9c3faed

Please sign in to comment.