Skip to content

Commit

Permalink
fix(symfony): use non deprecated validator exception (#6297)
Browse files Browse the repository at this point in the history
* fix(symfony): use non deprecated validator exception

* validation excetpion

* fix constraint violation list aware exception
;

* config
  • Loading branch information
soyuka committed Apr 18, 2024
1 parent af61482 commit 629da78
Show file tree
Hide file tree
Showing 11 changed files with 50 additions and 17 deletions.
Expand Up @@ -13,10 +13,11 @@

namespace ApiPlatform\GraphQl\Serializer\Exception;

use ApiPlatform\Metadata\Exception\RuntimeException;
use ApiPlatform\Symfony\Validator\Exception\ConstraintViolationListAwareExceptionInterface as LegacyConstraintViolationListAwareExceptionInterface;
use ApiPlatform\Validator\Exception\ConstraintViolationListAwareExceptionInterface;
use GraphQL\Error\Error;
use GraphQL\Error\FormattedError;
use Symfony\Component\Form\Exception\RuntimeException;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Serializer\Normalizer\NormalizerInterface;

Expand All @@ -38,7 +39,7 @@ public function __construct(private readonly array $exceptionToStatus = [])
public function normalize(mixed $object, ?string $format = null, array $context = []): array
{
$validationException = $object->getPrevious();
if (!$validationException instanceof ConstraintViolationListAwareExceptionInterface) {
if (!($validationException instanceof ConstraintViolationListAwareExceptionInterface || $validationException instanceof LegacyConstraintViolationListAwareExceptionInterface)) {
throw new RuntimeException(sprintf('Object is not a "%s".', ConstraintViolationListAwareExceptionInterface::class));
}

Expand Down
6 changes: 3 additions & 3 deletions src/JsonApi/Serializer/ErrorNormalizer.php
Expand Up @@ -15,8 +15,8 @@

use ApiPlatform\Problem\Serializer\ErrorNormalizerTrait;
use ApiPlatform\Serializer\CacheableSupportsMethodInterface;
use ApiPlatform\State\ApiResource\Error;
use ApiPlatform\Symfony\Validator\Exception\ConstraintViolationListAwareExceptionInterface;
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;
Expand Down Expand Up @@ -48,7 +48,7 @@ public function normalize(mixed $object, ?string $format = null, array $context
{
// 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 ConstraintViolationListAwareExceptionInterface) {
if ($object instanceof LegacyConstraintViolationListAwareExceptionInterface || $object instanceof ConstraintViolationListAwareExceptionInterface) {
// TODO: return ['errors' => $this->constraintViolationListNormalizer(...)]
return $this->constraintViolationListNormalizer->normalize($object->getConstraintViolationList(), $format, $context);
}
Expand Down
Expand Up @@ -816,6 +816,7 @@ private function getFormats(array $configFormats): array
private function registerValidatorConfiguration(ContainerBuilder $container, array $config, XmlFileLoader $loader): void
{
if (interface_exists(ValidatorInterface::class)) {
$container->setParameter('api_platform.validator.legacy_validation_exception', $config['validator']['legacy_validation_exception'] ?? true);
$loader->load('metadata/validator.xml');
$loader->load('validator/validator.xml');

Expand Down
3 changes: 3 additions & 0 deletions src/Symfony/Bundle/DependencyInjection/Configuration.php
Expand Up @@ -22,6 +22,8 @@
use ApiPlatform\Metadata\Put;
use ApiPlatform\ParameterValidator\Exception\ValidationExceptionInterface;
use ApiPlatform\Symfony\Controller\MainController;
use ApiPlatform\Symfony\Validator\Exception\ValidationException as LegacyValidationException;
use ApiPlatform\Validator\Exception\ValidationException;
use Doctrine\Bundle\DoctrineBundle\DoctrineBundle;
use Doctrine\Bundle\MongoDBBundle\DoctrineMongoDBBundle;
use Doctrine\ORM\EntityManagerInterface;
Expand Down Expand Up @@ -94,6 +96,7 @@ public function getConfigTreeBuilder(): TreeBuilder
->children()
->variableNode('serialize_payload_fields')->defaultValue([])->info('Set to null to serialize all payload fields when a validation error is thrown, or set the fields you want to include explicitly.')->end()
->booleanNode('query_parameter_validation')->defaultValue(true)->end()
->booleanNode('legacy_validation_exception')->defaultValue(true)->info('Uses the legacy "%s" instead of "%s".', LegacyValidationException::class, ValidationException::class)->end()
->end()
->end()
->arrayNode('eager_loading')
Expand Down
Expand Up @@ -8,6 +8,7 @@
<service id="api_platform.validator" class="ApiPlatform\Symfony\Validator\Validator">
<argument type="service" id="validator" />
<argument type="tagged_locator" tag="api_platform.validation_groups_generator" />
<argument>%api_platform.validator.legacy_validation_exception%</argument>
</service>
<service id="ApiPlatform\Validator\ValidatorInterface" alias="api_platform.validator" />

Expand Down
5 changes: 3 additions & 2 deletions src/Symfony/EventListener/ErrorListener.php
Expand Up @@ -26,7 +26,8 @@
use ApiPlatform\Metadata\Util\ContentNegotiationTrait;
use ApiPlatform\State\ApiResource\Error;
use ApiPlatform\State\Util\OperationRequestInitiatorTrait;
use ApiPlatform\Symfony\Util\RequestAttributesExtractor;
use ApiPlatform\State\Util\RequestAttributesExtractor;
use ApiPlatform\Symfony\Validator\Exception\ConstraintViolationListAwareExceptionInterface as LegacyConstraintViolationListAwareExceptionInterface;
use ApiPlatform\Validator\Exception\ConstraintViolationListAwareExceptionInterface;
use Negotiation\Negotiator;
use Psr\Log\LoggerInterface;
Expand Down Expand Up @@ -192,7 +193,7 @@ private function getStatusCode(?HttpOperation $apiOperation, Request $request, ?
return 400;
}

if ($exception instanceof ConstraintViolationListAwareExceptionInterface) {
if ($exception instanceof ConstraintViolationListAwareExceptionInterface || $exception instanceof LegacyConstraintViolationListAwareExceptionInterface) {
return 422;
}

Expand Down
Expand Up @@ -18,6 +18,8 @@

/**
* An exception which has a constraint violation list.
*
* @deprecated use ApiPlatform\Validator\Exception\ConstraintViolationListAwareExceptionInterface
*/
interface ConstraintViolationListAwareExceptionInterface extends ExceptionInterface
{
Expand Down
4 changes: 2 additions & 2 deletions src/Symfony/Validator/Exception/ValidationException.php
Expand Up @@ -18,7 +18,7 @@
use ApiPlatform\Metadata\ErrorResource;
use ApiPlatform\Metadata\Exception\HttpExceptionInterface;
use ApiPlatform\Metadata\Exception\ProblemExceptionInterface;
use ApiPlatform\Validator\Exception\ConstraintViolationListAwareExceptionInterface as ApiPlatformConstraintViolationListAwareExceptionInterface;
use ApiPlatform\Validator\Exception\ConstraintViolationListAwareExceptionInterface;
use ApiPlatform\Validator\Exception\ValidationException as BaseValidationException;
use Symfony\Component\HttpKernel\Exception\HttpExceptionInterface as SymfonyHttpExceptionInterface;
use Symfony\Component\WebLink\Link;
Expand Down Expand Up @@ -71,6 +71,6 @@
],
graphQlOperations: []
)]
final class ValidationException extends BaseValidationException implements ConstraintViolationListAwareExceptionInterface, ApiPlatformConstraintViolationListAwareExceptionInterface, \Stringable, ProblemExceptionInterface, HttpExceptionInterface, SymfonyHttpExceptionInterface
final class ValidationException extends BaseValidationException implements ConstraintViolationListAwareExceptionInterface, \Stringable, ProblemExceptionInterface, HttpExceptionInterface, SymfonyHttpExceptionInterface
{
}
8 changes: 6 additions & 2 deletions src/Symfony/Validator/Validator.php
Expand Up @@ -13,7 +13,8 @@

namespace ApiPlatform\Symfony\Validator;

use ApiPlatform\Symfony\Validator\Exception\ValidationException;
use ApiPlatform\Symfony\Validator\Exception\ValidationException as LegacyValidationException;
use ApiPlatform\Validator\Exception\ValidationException;
use ApiPlatform\Validator\ValidatorInterface;
use Psr\Container\ContainerInterface;
use Symfony\Component\Validator\Constraints\GroupSequence;
Expand All @@ -28,7 +29,7 @@
*/
class Validator implements ValidatorInterface
{
public function __construct(private readonly SymfonyValidatorInterface $validator, private readonly ?ContainerInterface $container = null)
public function __construct(private readonly SymfonyValidatorInterface $validator, private readonly ?ContainerInterface $container = null, private readonly ?bool $legacyValidationException = true)
{
}

Expand Down Expand Up @@ -57,6 +58,9 @@ public function validate(object $data, array $context = []): void

$violations = $this->validator->validate($data, null, $validationGroups);
if (0 !== \count($violations)) {
if (true === $this->legacyValidationException) {
throw new LegacyValidationException($violations);
}
throw new ValidationException($violations);
}
}
Expand Down
Expand Up @@ -109,6 +109,7 @@ private function runDefaultConfigTests(array $doctrineIntegrationsToLoad = ['orm
'validator' => [
'serialize_payload_fields' => [],
'query_parameter_validation' => true,
'legacy_validation_exception' => true,
],
'name_converter' => null,
'enable_swagger' => true,
Expand Down
31 changes: 25 additions & 6 deletions tests/Symfony/Validator/ValidatorTest.php
Expand Up @@ -13,10 +13,11 @@

namespace ApiPlatform\Tests\Symfony\Validator;

use ApiPlatform\Symfony\Validator\Exception\ValidationException;
use ApiPlatform\Symfony\Validator\Exception\ValidationException as LegacyValidationException;
use ApiPlatform\Symfony\Validator\ValidationGroupsGeneratorInterface;
use ApiPlatform\Symfony\Validator\Validator;
use ApiPlatform\Tests\Fixtures\DummyEntity;
use ApiPlatform\Validator\Exception\ValidationException;
use PHPUnit\Framework\TestCase;
use Prophecy\PhpUnit\ProphecyTrait;
use Psr\Container\ContainerInterface;
Expand All @@ -43,7 +44,7 @@ public function testValid(): void
$symfonyValidatorProphecy->validate($data, null, null)->willReturn($constraintViolationListProphecy->reveal())->shouldBeCalled();
$symfonyValidator = $symfonyValidatorProphecy->reveal();

$validator = new Validator($symfonyValidator);
$validator = new Validator($symfonyValidator, legacyValidationException: false);
$validator->validate(new DummyEntity());
}

Expand All @@ -58,7 +59,25 @@ public function testInvalid(): void
$symfonyValidatorProphecy->validate($data, null, null)->willReturn($constraintViolationList)->shouldBeCalled();
$symfonyValidator = $symfonyValidatorProphecy->reveal();

$validator = new Validator($symfonyValidator);
$validator = new Validator($symfonyValidator, legacyValidationException: false);
$validator->validate(new DummyEntity());
}

/**
* @group legacy
*/
public function testDeprecatedInvalid(): void
{
$this->expectException(LegacyValidationException::class);

$data = new DummyEntity();
$constraintViolationList = new ConstraintViolationList([new ConstraintViolation('test', null, [], null, 'test', null), new ConstraintViolation('test', null, [], null, 'test', null)]);

$symfonyValidatorProphecy = $this->prophesize(SymfonyValidatorInterface::class);
$symfonyValidatorProphecy->validate($data, null, null)->willReturn($constraintViolationList)->shouldBeCalled();
$symfonyValidator = $symfonyValidatorProphecy->reveal();

$validator = new Validator($symfonyValidator, legacyValidationException: true);
$validator->validate(new DummyEntity());
}

Expand All @@ -74,7 +93,7 @@ public function testGetGroupsFromCallable(): void
$symfonyValidatorProphecy->validate($data, null, $expectedValidationGroups)->willReturn($constraintViolationListProphecy->reveal())->shouldBeCalled();
$symfonyValidator = $symfonyValidatorProphecy->reveal();

$validator = new Validator($symfonyValidator);
$validator = new Validator($symfonyValidator, legacyValidationException: false);
$validator->validate(new DummyEntity(), ['groups' => fn ($data): array => $data instanceof DummyEntity ? $expectedValidationGroups : []]);
}

Expand All @@ -97,7 +116,7 @@ public function __invoke(object $object): array
}
});

$validator = new Validator($symfonyValidatorProphecy->reveal(), $containerProphecy->reveal());
$validator = new Validator($symfonyValidatorProphecy->reveal(), $containerProphecy->reveal(), legacyValidationException: false);
$validator->validate(new DummyEntity(), ['groups' => 'groups_builder']);
}

Expand All @@ -116,7 +135,7 @@ public function testValidatorWithScalarGroup(): void
$containerProphecy = $this->prophesize(ContainerInterface::class);
$containerProphecy->has('foo')->willReturn(false)->shouldBeCalled();

$validator = new Validator($symfonyValidator, $containerProphecy->reveal());
$validator = new Validator($symfonyValidator, $containerProphecy->reveal(), legacyValidationException: false);
$validator->validate(new DummyEntity(), ['groups' => 'foo']);
}
}

0 comments on commit 629da78

Please sign in to comment.