Skip to content

Commit

Permalink
Merge 3.4
Browse files Browse the repository at this point in the history
  • Loading branch information
soyuka committed Dec 13, 2024
2 parents 6f86528 + dd831ff commit 1977783
Show file tree
Hide file tree
Showing 12 changed files with 76 additions and 141 deletions.
3 changes: 1 addition & 2 deletions JsonSchema/SchemaFactory.php
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,6 @@

namespace ApiPlatform\JsonApi\JsonSchema;

use ApiPlatform\Api\ResourceClassResolverInterface as LegacyResourceClassResolverInterface;
use ApiPlatform\JsonSchema\DefinitionNameFactoryInterface;
use ApiPlatform\JsonSchema\ResourceMetadataTrait;
use ApiPlatform\JsonSchema\Schema;
Expand Down Expand Up @@ -106,7 +105,7 @@ final class SchemaFactory implements SchemaFactoryInterface, SchemaFactoryAwareI
],
];

public function __construct(private readonly SchemaFactoryInterface $schemaFactory, private readonly PropertyMetadataFactoryInterface $propertyMetadataFactory, ResourceClassResolverInterface|LegacyResourceClassResolverInterface $resourceClassResolver, ?ResourceMetadataCollectionFactoryInterface $resourceMetadataFactory = null, private readonly ?DefinitionNameFactoryInterface $definitionNameFactory = null)
public function __construct(private readonly SchemaFactoryInterface $schemaFactory, private readonly PropertyMetadataFactoryInterface $propertyMetadataFactory, ResourceClassResolverInterface $resourceClassResolver, ?ResourceMetadataCollectionFactoryInterface $resourceMetadataFactory = null, private readonly ?DefinitionNameFactoryInterface $definitionNameFactory = null)
{
if ($this->schemaFactory instanceof SchemaFactoryAwareInterface) {
$this->schemaFactory->setSchemaFactory($this);
Expand Down
3 changes: 1 addition & 2 deletions Serializer/CollectionNormalizer.php
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,6 @@

namespace ApiPlatform\JsonApi\Serializer;

use ApiPlatform\Api\ResourceClassResolverInterface as LegacyResourceClassResolverInterface;
use ApiPlatform\Metadata\Resource\Factory\ResourceMetadataCollectionFactoryInterface;
use ApiPlatform\Metadata\ResourceClassResolverInterface;
use ApiPlatform\Metadata\Util\IriHelper;
Expand All @@ -31,7 +30,7 @@ final class CollectionNormalizer extends AbstractCollectionNormalizer
{
public const FORMAT = 'jsonapi';

public function __construct(ResourceClassResolverInterface|LegacyResourceClassResolverInterface $resourceClassResolver, string $pageParameterName, ResourceMetadataCollectionFactoryInterface $resourceMetadataFactory)
public function __construct(ResourceClassResolverInterface $resourceClassResolver, string $pageParameterName, ResourceMetadataCollectionFactoryInterface $resourceMetadataFactory)
{
parent::__construct($resourceClassResolver, $pageParameterName, $resourceMetadataFactory);
}
Expand Down
18 changes: 1 addition & 17 deletions Serializer/ConstraintViolationListNormalizer.php
Original file line number Diff line number Diff line change
Expand Up @@ -14,10 +14,8 @@
namespace ApiPlatform\JsonApi\Serializer;

use ApiPlatform\Metadata\Property\Factory\PropertyMetadataFactoryInterface;
use ApiPlatform\Serializer\CacheableSupportsMethodInterface;
use Symfony\Component\Serializer\NameConverter\NameConverterInterface;
use Symfony\Component\Serializer\Normalizer\NormalizerInterface;
use Symfony\Component\Serializer\Serializer;
use Symfony\Component\Validator\ConstraintViolationInterface;
use Symfony\Component\Validator\ConstraintViolationListInterface;

Expand All @@ -26,7 +24,7 @@
*
* @author Héctor Hurtarte <[email protected]>
*/
final class ConstraintViolationListNormalizer implements NormalizerInterface, CacheableSupportsMethodInterface
final class ConstraintViolationListNormalizer implements NormalizerInterface
{
public const FORMAT = 'jsonapi';

Expand Down Expand Up @@ -65,20 +63,6 @@ public function getSupportedTypes($format): array
return self::FORMAT === $format ? [ConstraintViolationListInterface::class => true] : [];
}

public function hasCacheableSupportsMethod(): bool
{
if (method_exists(Serializer::class, 'getSupportedTypes')) {
trigger_deprecation(
'api-platform/core',
'3.1',
'The "%s()" method is deprecated, use "getSupportedTypes()" instead.',
__METHOD__
);
}

return true;
}

private function getSourcePointerFromViolation(ConstraintViolationInterface $violation): string
{
$fieldName = $violation->getPropertyPath();
Expand Down
29 changes: 5 additions & 24 deletions Serializer/EntrypointNormalizer.php
Original file line number Diff line number Diff line change
Expand Up @@ -13,31 +13,26 @@

namespace ApiPlatform\JsonApi\Serializer;

use ApiPlatform\Api\Entrypoint;
use ApiPlatform\Api\IriConverterInterface as LegacyIriConverterInterface;
use ApiPlatform\Api\UrlGeneratorInterface as LegacyUrlGeneratorInterface;
use ApiPlatform\Documentation\Entrypoint as DocumentationEntrypoint;
use ApiPlatform\Documentation\Entrypoint;
use ApiPlatform\Metadata\CollectionOperationInterface;
use ApiPlatform\Metadata\Exception\InvalidArgumentException;
use ApiPlatform\Metadata\HttpOperation;
use ApiPlatform\Metadata\IriConverterInterface;
use ApiPlatform\Metadata\Resource\Factory\ResourceMetadataCollectionFactoryInterface;
use ApiPlatform\Metadata\UrlGeneratorInterface;
use ApiPlatform\Serializer\CacheableSupportsMethodInterface;
use Symfony\Component\Serializer\Normalizer\NormalizerInterface;
use Symfony\Component\Serializer\Serializer;

/**
* Normalizes the API entrypoint.
*
* @author Amrouche Hamza <[email protected]>
* @author Kévin Dunglas <[email protected]>
*/
final class EntrypointNormalizer implements NormalizerInterface, CacheableSupportsMethodInterface
final class EntrypointNormalizer implements NormalizerInterface
{
public const FORMAT = 'jsonapi';

public function __construct(private readonly ResourceMetadataCollectionFactoryInterface $resourceMetadataFactory, private readonly IriConverterInterface|LegacyIriConverterInterface $iriConverter, private readonly UrlGeneratorInterface|LegacyUrlGeneratorInterface $urlGenerator)
public function __construct(private readonly ResourceMetadataCollectionFactoryInterface $resourceMetadataFactory, private readonly IriConverterInterface $iriConverter, private readonly UrlGeneratorInterface $urlGenerator)
{
}

Expand Down Expand Up @@ -75,25 +70,11 @@ public function normalize(mixed $object, ?string $format = null, array $context
*/
public function supportsNormalization(mixed $data, ?string $format = null, array $context = []): bool
{
return self::FORMAT === $format && ($data instanceof Entrypoint || $data instanceof DocumentationEntrypoint);
return self::FORMAT === $format && $data instanceof Entrypoint;
}

public function getSupportedTypes($format): array
{
return self::FORMAT === $format ? [Entrypoint::class => true, DocumentationEntrypoint::class => true] : [];
}

public function hasCacheableSupportsMethod(): bool
{
if (method_exists(Serializer::class, 'getSupportedTypes')) {
trigger_deprecation(
'api-platform/core',
'3.1',
'The "%s()" method is deprecated, use "getSupportedTypes()" instead.',
__METHOD__
);
}

return true;
return self::FORMAT === $format ? [Entrypoint::class => true] : [];
}
}
75 changes: 26 additions & 49 deletions Serializer/ErrorNormalizer.php
Original file line number Diff line number Diff line change
Expand Up @@ -13,67 +13,58 @@

namespace ApiPlatform\JsonApi\Serializer;

use ApiPlatform\Serializer\CacheableSupportsMethodInterface;
use ApiPlatform\Symfony\Validator\Exception\ConstraintViolationListAwareExceptionInterface as LegacyConstraintViolationListAwareExceptionInterface;
use ApiPlatform\Validator\Exception\ConstraintViolationListAwareExceptionInterface;
use Symfony\Component\ErrorHandler\Exception\FlattenException;
use Symfony\Component\Serializer\Normalizer\NormalizerInterface;
use Symfony\Component\Serializer\Serializer;

/**
* Converts {@see \Exception} or {@see FlattenException} or to a JSON API error representation.
*
* @author Héctor Hurtarte <[email protected]>
*/
final class ErrorNormalizer implements NormalizerInterface, CacheableSupportsMethodInterface
final class ErrorNormalizer implements NormalizerInterface
{
use ErrorNormalizerTrait;

public const FORMAT = 'jsonapi';
public const TITLE = 'title';
private array $defaultContext = [
self::TITLE => 'An error occurred',
];

public function __construct(private readonly bool $debug = false, array $defaultContext = [], private ?NormalizerInterface $itemNormalizer = null, private ?NormalizerInterface $constraintViolationListNormalizer = null)
public function __construct(private ?NormalizerInterface $itemNormalizer = null)
{
$this->defaultContext = array_merge($this->defaultContext, $defaultContext);
}

/**
* {@inheritdoc}
*/
public function normalize(mixed $object, ?string $format = null, array $context = []): array
{
// TODO: in api platform 4 this will be the default, note that JSON:API is close to Problem so we should use the same normalizer
if ($context['rfc_7807_compliant_errors'] ?? false) {
if ($object instanceof LegacyConstraintViolationListAwareExceptionInterface || $object instanceof ConstraintViolationListAwareExceptionInterface) {
// TODO: return ['errors' => $this->constraintViolationListNormalizer(...)]
return $this->constraintViolationListNormalizer->normalize($object->getConstraintViolationList(), $format, $context);
}

$jsonApiObject = $this->itemNormalizer->normalize($object, $format, $context);
$error = $jsonApiObject['data']['attributes'];
$error['id'] = $jsonApiObject['data']['id'];
$error['type'] = $jsonApiObject['data']['id'];

return ['errors' => [$error]];
$jsonApiObject = $this->itemNormalizer->normalize($object, $format, $context);
$error = $jsonApiObject['data']['attributes'];
$error['id'] = $jsonApiObject['data']['id'];
if (isset($error['type'])) {
$error['links'] = ['type' => $error['type']];
}

$data = [
'title' => $context[self::TITLE] ?? $this->defaultContext[self::TITLE],
'description' => $this->getErrorMessage($object, $context, $this->debug),
];
if (!isset($error['code']) && method_exists($object, 'getId')) {
$error['code'] = $object->getId();
}

if (null !== $errorCode = $this->getErrorCode($object)) {
$data['code'] = $errorCode;
if (!isset($error['violations'])) {
return ['errors' => [$error]];
}

if ($this->debug && null !== $trace = $object->getTrace()) {
$data['trace'] = $trace;
$errors = [];
foreach ($error['violations'] as $violation) {
$e = ['detail' => $violation['message']] + $error;
if (isset($error['links']['type'])) {
$type = $error['links']['type'];
$e['links']['type'] = \sprintf('%s/%s', $type, $violation['propertyPath']);
$e['id'] = str_replace($type, $e['links']['type'], $e['id']);
}
if (isset($e['code'])) {
$e['code'] = \sprintf('%s/%s', $error['code'], $violation['propertyPath']);
}
unset($e['violations']);
$errors[] = $e;
}

return $data;
return ['errors' => $errors];
}

/**
Expand All @@ -95,18 +86,4 @@ public function getSupportedTypes($format): array

return [];
}

public function hasCacheableSupportsMethod(): bool
{
if (method_exists(Serializer::class, 'getSupportedTypes')) {
trigger_deprecation(
'api-platform/core',
'3.1',
'The "%s()" method is deprecated, use "getSupportedTypes()" instead.',
__METHOD__
);
}

return true;
}
}
6 changes: 2 additions & 4 deletions Serializer/ItemNormalizer.php
Original file line number Diff line number Diff line change
Expand Up @@ -13,22 +13,20 @@

namespace ApiPlatform\JsonApi\Serializer;

use ApiPlatform\Api\IriConverterInterface as LegacyIriConverterInterface;
use ApiPlatform\Api\ResourceClassResolverInterface as LegacyResourceClassResolverInterface;
use ApiPlatform\Metadata\ApiProperty;
use ApiPlatform\Metadata\Exception\ItemNotFoundException;
use ApiPlatform\Metadata\IriConverterInterface;
use ApiPlatform\Metadata\Property\Factory\PropertyMetadataFactoryInterface;
use ApiPlatform\Metadata\Property\Factory\PropertyNameCollectionFactoryInterface;
use ApiPlatform\Metadata\Resource\Factory\ResourceMetadataCollectionFactoryInterface;
use ApiPlatform\Metadata\ResourceAccessCheckerInterface;
use ApiPlatform\Metadata\ResourceClassResolverInterface;
use ApiPlatform\Metadata\UrlGeneratorInterface;
use ApiPlatform\Metadata\Util\ClassInfoTrait;
use ApiPlatform\Serializer\AbstractItemNormalizer;
use ApiPlatform\Serializer\CacheKeyTrait;
use ApiPlatform\Serializer\ContextTrait;
use ApiPlatform\Serializer\TagCollectorInterface;
use ApiPlatform\Symfony\Security\ResourceAccessCheckerInterface;
use Symfony\Component\ErrorHandler\Exception\FlattenException;
use Symfony\Component\PropertyAccess\PropertyAccessorInterface;
use Symfony\Component\Serializer\Exception\LogicException;
Expand Down Expand Up @@ -56,7 +54,7 @@ final class ItemNormalizer extends AbstractItemNormalizer

private array $componentsCache = [];

public function __construct(PropertyNameCollectionFactoryInterface $propertyNameCollectionFactory, PropertyMetadataFactoryInterface $propertyMetadataFactory, IriConverterInterface|LegacyIriConverterInterface $iriConverter, ResourceClassResolverInterface|LegacyResourceClassResolverInterface $resourceClassResolver, ?PropertyAccessorInterface $propertyAccessor = null, ?NameConverterInterface $nameConverter = null, ?ClassMetadataFactoryInterface $classMetadataFactory = null, array $defaultContext = [], ?ResourceMetadataCollectionFactoryInterface $resourceMetadataCollectionFactory = null, ?ResourceAccessCheckerInterface $resourceAccessChecker = null, protected ?TagCollectorInterface $tagCollector = null)
public function __construct(PropertyNameCollectionFactoryInterface $propertyNameCollectionFactory, PropertyMetadataFactoryInterface $propertyMetadataFactory, IriConverterInterface $iriConverter, ResourceClassResolverInterface $resourceClassResolver, ?PropertyAccessorInterface $propertyAccessor = null, ?NameConverterInterface $nameConverter = null, ?ClassMetadataFactoryInterface $classMetadataFactory = null, array $defaultContext = [], ?ResourceMetadataCollectionFactoryInterface $resourceMetadataCollectionFactory = null, ?ResourceAccessCheckerInterface $resourceAccessChecker = null, protected ?TagCollectorInterface $tagCollector = null)
{
parent::__construct($propertyNameCollectionFactory, $propertyMetadataFactory, $iriConverter, $resourceClassResolver, $propertyAccessor, $nameConverter, $classMetadataFactory, $defaultContext, $resourceMetadataCollectionFactory, $resourceAccessChecker, $tagCollector);
}
Expand Down
30 changes: 2 additions & 28 deletions Serializer/ObjectNormalizer.php
Original file line number Diff line number Diff line change
Expand Up @@ -13,28 +13,23 @@

namespace ApiPlatform\JsonApi\Serializer;

use ApiPlatform\Api\IriConverterInterface as LegacyIriConverterInterface;
use ApiPlatform\Api\ResourceClassResolverInterface as LegacyResourceClassResolverInterface;
use ApiPlatform\Metadata\IriConverterInterface;
use ApiPlatform\Metadata\Resource\Factory\ResourceMetadataCollectionFactoryInterface;
use ApiPlatform\Metadata\ResourceClassResolverInterface;
use ApiPlatform\Metadata\Util\ClassInfoTrait;
use ApiPlatform\Serializer\CacheableSupportsMethodInterface;
use Symfony\Component\Serializer\Normalizer\CacheableSupportsMethodInterface as BaseCacheableSupportsMethodInterface;
use Symfony\Component\Serializer\Normalizer\NormalizerInterface;
use Symfony\Component\Serializer\Serializer;

/**
* Decorates the output with JSON API metadata when appropriate, but otherwise
* just passes through to the decorated normalizer.
*/
final class ObjectNormalizer implements NormalizerInterface, CacheableSupportsMethodInterface
final class ObjectNormalizer implements NormalizerInterface
{
use ClassInfoTrait;

public const FORMAT = 'jsonapi';

public function __construct(private readonly NormalizerInterface $decorated, private readonly IriConverterInterface|LegacyIriConverterInterface $iriConverter, private readonly ResourceClassResolverInterface|LegacyResourceClassResolverInterface $resourceClassResolver, private readonly ResourceMetadataCollectionFactoryInterface $resourceMetadataFactory)
public function __construct(private readonly NormalizerInterface $decorated, private readonly IriConverterInterface $iriConverter, private readonly ResourceClassResolverInterface $resourceClassResolver, private readonly ResourceMetadataCollectionFactoryInterface $resourceMetadataFactory)
{
}

Expand All @@ -48,30 +43,9 @@ public function supportsNormalization(mixed $data, ?string $format = null, array

public function getSupportedTypes($format): array
{
// @deprecated remove condition when support for symfony versions under 6.3 is dropped
if (!method_exists($this->decorated, 'getSupportedTypes')) {
return [
'*' => $this->decorated instanceof BaseCacheableSupportsMethodInterface && $this->decorated->hasCacheableSupportsMethod(),
];
}

return self::FORMAT === $format ? $this->decorated->getSupportedTypes($format) : [];
}

public function hasCacheableSupportsMethod(): bool
{
if (method_exists(Serializer::class, 'getSupportedTypes')) {
trigger_deprecation(
'api-platform/core',
'3.1',
'The "%s()" method is deprecated, use "getSupportedTypes()" instead.',
__METHOD__
);
}

return $this->decorated instanceof BaseCacheableSupportsMethodInterface && $this->decorated->hasCacheableSupportsMethod();
}

/**
* {@inheritdoc}
*/
Expand Down
5 changes: 5 additions & 0 deletions Serializer/ReservedAttributeNameConverter.php
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@

namespace ApiPlatform\JsonApi\Serializer;

use ApiPlatform\Metadata\Exception\ProblemExceptionInterface;
use Symfony\Component\Serializer\NameConverter\AdvancedNameConverterInterface;
use Symfony\Component\Serializer\NameConverter\NameConverterInterface;

Expand Down Expand Up @@ -44,6 +45,10 @@ public function normalize(string $propertyName, ?string $class = null, ?string $
$propertyName = $this->nameConverter->normalize($propertyName, $class, $format, $context);
}

if ($class && is_a($class, ProblemExceptionInterface::class, true)) {
return $propertyName;
}

if (isset(self::JSON_API_RESERVED_ATTRIBUTES[$propertyName])) {
$propertyName = self::JSON_API_RESERVED_ATTRIBUTES[$propertyName];
}
Expand Down
27 changes: 27 additions & 0 deletions Tests/Fixtures/ErrorCodeSerializable.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
<?php

/*
* This file is part of the API Platform project.
*
* (c) Kévin Dunglas <[email protected]>
*
* 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\JsonApi\Tests\Fixtures;

use ApiPlatform\Metadata\Exception\ErrorCodeSerializableInterface;

class ErrorCodeSerializable extends \Exception implements ErrorCodeSerializableInterface
{
/**
* {@inheritdoc}
*/
public static function getErrorCode(): string
{
return '1234';
}
}
Loading

0 comments on commit 1977783

Please sign in to comment.