mirror of
https://github.com/retailcrm/NelmioApiDocBundle.git
synced 2025-02-02 23:59:26 +03:00
Add Validation Group Support to SymfonyConstraintAnnotationReader
This support is gated behind a flag as turning it on seems like it would be a large backwards incompatible change.
This commit is contained in:
parent
0ed76d7d24
commit
cc962b72c8
@ -52,6 +52,6 @@ class AnnotationsReader
|
|||||||
{
|
{
|
||||||
$this->openApiAnnotationsReader->updateProperty($reflection, $property, $serializationGroups);
|
$this->openApiAnnotationsReader->updateProperty($reflection, $property, $serializationGroups);
|
||||||
$this->phpDocReader->updateProperty($reflection, $property);
|
$this->phpDocReader->updateProperty($reflection, $property);
|
||||||
$this->symfonyConstraintAnnotationReader->updateProperty($reflection, $property);
|
$this->symfonyConstraintAnnotationReader->updateProperty($reflection, $property, $serializationGroups);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -32,9 +32,15 @@ class SymfonyConstraintAnnotationReader
|
|||||||
*/
|
*/
|
||||||
private $schema;
|
private $schema;
|
||||||
|
|
||||||
public function __construct(Reader $annotationsReader)
|
/**
|
||||||
|
* @var bool
|
||||||
|
*/
|
||||||
|
private $useValidationGroups;
|
||||||
|
|
||||||
|
public function __construct(Reader $annotationsReader, bool $useValidationGroups=false)
|
||||||
{
|
{
|
||||||
$this->annotationsReader = $annotationsReader;
|
$this->annotationsReader = $annotationsReader;
|
||||||
|
$this->useValidationGroups = $useValidationGroups;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -42,9 +48,9 @@ class SymfonyConstraintAnnotationReader
|
|||||||
*
|
*
|
||||||
* @param \ReflectionProperty|\ReflectionMethod $reflection
|
* @param \ReflectionProperty|\ReflectionMethod $reflection
|
||||||
*/
|
*/
|
||||||
public function updateProperty($reflection, OA\Property $property): void
|
public function updateProperty($reflection, OA\Property $property, ?array $validationGroups = null): void
|
||||||
{
|
{
|
||||||
foreach ($this->getAnnotations($reflection) as $outerAnnotation) {
|
foreach ($this->getAnnotations($reflection, $validationGroups) as $outerAnnotation) {
|
||||||
$innerAnnotations = $outerAnnotation instanceof Assert\Compound
|
$innerAnnotations = $outerAnnotation instanceof Assert\Compound
|
||||||
? $outerAnnotation->constraints
|
? $outerAnnotation->constraints
|
||||||
: [$outerAnnotation];
|
: [$outerAnnotation];
|
||||||
@ -175,7 +181,23 @@ class SymfonyConstraintAnnotationReader
|
|||||||
/**
|
/**
|
||||||
* @param \ReflectionProperty|\ReflectionMethod $reflection
|
* @param \ReflectionProperty|\ReflectionMethod $reflection
|
||||||
*/
|
*/
|
||||||
private function getAnnotations($reflection): \Traversable
|
private function getAnnotations($reflection, ?array $validationGroups): iterable
|
||||||
|
{
|
||||||
|
foreach ($this->locateAnnotations($reflection) as $annotation) {
|
||||||
|
if (! $annotation instanceof Constraint) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (! $this->useValidationGroups || $this->isConstraintInGroup($annotation, $validationGroups)) {
|
||||||
|
yield $annotation;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param \ReflectionProperty|\ReflectionMethod $reflection
|
||||||
|
*/
|
||||||
|
private function locateAnnotations($reflection): \Traversable
|
||||||
{
|
{
|
||||||
if (\PHP_VERSION_ID >= 80000 && class_exists(Constraint::class)) {
|
if (\PHP_VERSION_ID >= 80000 && class_exists(Constraint::class)) {
|
||||||
foreach ($reflection->getAttributes(Constraint::class, \ReflectionAttribute::IS_INSTANCEOF) as $attribute) {
|
foreach ($reflection->getAttributes(Constraint::class, \ReflectionAttribute::IS_INSTANCEOF) as $attribute) {
|
||||||
@ -189,4 +211,20 @@ class SymfonyConstraintAnnotationReader
|
|||||||
yield from $this->annotationsReader->getMethodAnnotations($reflection);
|
yield from $this->annotationsReader->getMethodAnnotations($reflection);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check to see if the given constraint is in the provided serialization groups.
|
||||||
|
*
|
||||||
|
* If no groups are provided the validator would run in the Constraint::DEFAULT_GROUP,
|
||||||
|
* and constraints without any `groups` passed to them would be in that same
|
||||||
|
* default group. So even with a null $validationGroups passed here there still
|
||||||
|
* has to be a check on the default group.
|
||||||
|
*/
|
||||||
|
private function isConstraintInGroup(Constraint $annotation, ?array $validationGroups) : bool
|
||||||
|
{
|
||||||
|
return count(array_intersect(
|
||||||
|
$validationGroups ?: [Constraint::DEFAULT_GROUP],
|
||||||
|
$annotation->groups
|
||||||
|
)) > 0;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -12,6 +12,7 @@
|
|||||||
namespace Nelmio\ApiDocBundle\Tests\ModelDescriber\Annotations;
|
namespace Nelmio\ApiDocBundle\Tests\ModelDescriber\Annotations;
|
||||||
|
|
||||||
use Doctrine\Common\Annotations\AnnotationReader;
|
use Doctrine\Common\Annotations\AnnotationReader;
|
||||||
|
use Symfony\Component\Validator\Constraint;
|
||||||
use Nelmio\ApiDocBundle\ModelDescriber\Annotations\SymfonyConstraintAnnotationReader;
|
use Nelmio\ApiDocBundle\ModelDescriber\Annotations\SymfonyConstraintAnnotationReader;
|
||||||
use Nelmio\ApiDocBundle\Tests\Helper;
|
use Nelmio\ApiDocBundle\Tests\Helper;
|
||||||
use Nelmio\ApiDocBundle\Tests\ModelDescriber\Annotations\Fixture as CustomAssert;
|
use Nelmio\ApiDocBundle\Tests\ModelDescriber\Annotations\Fixture as CustomAssert;
|
||||||
@ -423,4 +424,125 @@ class SymfonyConstraintAnnotationReaderTest extends TestCase
|
|||||||
}];
|
}];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* re-using another provider here, since all constraints land in the default
|
||||||
|
* group when `group={"someGroup"}` is not set.
|
||||||
|
*
|
||||||
|
* @group https://github.com/nelmio/NelmioApiDocBundle/issues/1857
|
||||||
|
* @dataProvider provideRangeConstraintDoesNotSetMinimumIfMinIsNotSet
|
||||||
|
*/
|
||||||
|
public function testReaderWithValidationGroupsEnabledChecksForDefaultGroupWhenNoSerializationGroupsArePassed($entity)
|
||||||
|
{
|
||||||
|
$schema = new OA\Schema([]);
|
||||||
|
$schema->merge([new OA\Property(['property' => 'property1'])]);
|
||||||
|
$reader = $this->createConstraintReaderWithValidationGroupsEnabled();
|
||||||
|
$reader->setSchema($schema);
|
||||||
|
|
||||||
|
// no serialization groups passed here
|
||||||
|
$reader->updateProperty(
|
||||||
|
new \ReflectionProperty($entity, 'property1'),
|
||||||
|
$schema->properties[0]
|
||||||
|
);
|
||||||
|
|
||||||
|
$this->assertSame(10, $schema->properties[0]->maximum, 'should have read constraints in the default group');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @group https://github.com/nelmio/NelmioApiDocBundle/issues/1857
|
||||||
|
* @dataProvider provideConstraintsWithGroups
|
||||||
|
*/
|
||||||
|
public function testReaderWithValidationGroupsEnabledDoesNotReadAnnotationsWithoutDefaultGroupIfNoGroupsArePassed($entity)
|
||||||
|
{
|
||||||
|
$schema = new OA\Schema([]);
|
||||||
|
$schema->merge([
|
||||||
|
new OA\Property(['property' => 'property1']),
|
||||||
|
]);
|
||||||
|
$reader = $this->createConstraintReaderWithValidationGroupsEnabled();
|
||||||
|
$reader->setSchema($schema);
|
||||||
|
|
||||||
|
// no serialization groups passed here
|
||||||
|
$reader->updateProperty(
|
||||||
|
new \ReflectionProperty($entity, 'property1'),
|
||||||
|
$schema->properties[0]
|
||||||
|
);
|
||||||
|
|
||||||
|
$this->assertSame(['property1'], $schema->required, 'should have read constraint in default group');
|
||||||
|
$this->assertSame(OA\UNDEFINED, $schema->properties[0]->minimum, 'should not have read constraint in other group');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @group https://github.com/nelmio/NelmioApiDocBundle/issues/1857
|
||||||
|
* @dataProvider provideConstraintsWithGroups
|
||||||
|
*/
|
||||||
|
public function testReaderWithValidationGroupsEnabledReadsOnlyConstraintsWithGroupsProvided($entity)
|
||||||
|
{
|
||||||
|
$schema = new OA\Schema([]);
|
||||||
|
$schema->merge([
|
||||||
|
new OA\Property(['property' => 'property1']),
|
||||||
|
]);
|
||||||
|
$reader = $this->createConstraintReaderWithValidationGroupsEnabled();
|
||||||
|
$reader->setSchema($schema);
|
||||||
|
|
||||||
|
// no serialization groups passed here
|
||||||
|
$reader->updateProperty(
|
||||||
|
new \ReflectionProperty($entity, 'property1'),
|
||||||
|
$schema->properties[0],
|
||||||
|
["other"],
|
||||||
|
);
|
||||||
|
|
||||||
|
$this->assertSame(OA\UNDEFINED, $schema->required, 'should not have read constraint in default group');
|
||||||
|
$this->assertSame(1, $schema->properties[0]->minimum, 'should have read constraint in other group');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @group https://github.com/nelmio/NelmioApiDocBundle/issues/1857
|
||||||
|
* @dataProvider provideConstraintsWithGroups
|
||||||
|
*/
|
||||||
|
public function testReaderWithValidationGroupsEnabledCanReadFromMultipleValidationGroups($entity)
|
||||||
|
{
|
||||||
|
$schema = new OA\Schema([]);
|
||||||
|
$schema->merge([
|
||||||
|
new OA\Property(['property' => 'property1']),
|
||||||
|
]);
|
||||||
|
$reader = $this->createConstraintReaderWithValidationGroupsEnabled();
|
||||||
|
$reader->setSchema($schema);
|
||||||
|
|
||||||
|
// no serialization groups passed here
|
||||||
|
$reader->updateProperty(
|
||||||
|
new \ReflectionProperty($entity, 'property1'),
|
||||||
|
$schema->properties[0],
|
||||||
|
["other", Constraint::DEFAULT_GROUP],
|
||||||
|
);
|
||||||
|
|
||||||
|
$this->assertSame(['property1'], $schema->required, 'should have read constraint in default group');
|
||||||
|
$this->assertSame(1, $schema->properties[0]->minimum, 'should have read constraint in other group');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function provideConstraintsWithGroups(): iterable
|
||||||
|
{
|
||||||
|
yield 'Annotations' => [new class() {
|
||||||
|
/**
|
||||||
|
* @Assert\NotBlank()
|
||||||
|
* @Assert\Range(min=1, groups={"other"})
|
||||||
|
*/
|
||||||
|
private $property1;
|
||||||
|
}];
|
||||||
|
|
||||||
|
if (\PHP_VERSION_ID >= 80000) {
|
||||||
|
yield 'Attributes' => [new class() {
|
||||||
|
#[Assert\NotBlank()]
|
||||||
|
#[Assert\Range(min: 1, group: ["other"])]
|
||||||
|
private $property1;
|
||||||
|
}];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private function createConstraintReaderWithValidationGroupsEnabled() : SymfonyConstraintAnnotationReader
|
||||||
|
{
|
||||||
|
return new SymfonyConstraintAnnotationReader(
|
||||||
|
new AnnotationReader(),
|
||||||
|
true
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user