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 authored Apr 18, 2024
1 parent af61482 commit 629da78
Show file tree
Hide file tree
Showing 11 changed files with 50 additions and 17 deletions.
Original file line number Diff line number Diff line change
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
Original file line number Diff line number Diff line change
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
Original file line number Diff line number Diff line change
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
Original file line number Diff line number Diff line change
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
Original file line number Diff line number Diff line change
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
Original file line number Diff line number Diff line change
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
Original file line number Diff line number Diff line change
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
Original file line number Diff line number Diff line change
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
Original file line number Diff line number Diff line change
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
Original file line number Diff line number Diff line change
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
Original file line number Diff line number Diff line change
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.