Skip to content

Commit

Permalink
Added support for constraint attributes
Browse files Browse the repository at this point in the history
  • Loading branch information
derrabus committed Mar 11, 2021
1 parent 8f646b8 commit 7d124d6
Show file tree
Hide file tree
Showing 2 changed files with 135 additions and 42 deletions.
27 changes: 25 additions & 2 deletions ModelDescriber/Annotations/SymfonyConstraintAnnotationReader.php
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
use Doctrine\Common\Annotations\Reader;
use Nelmio\ApiDocBundle\OpenApiPhp\Util;
use OpenApi\Annotations as OA;
use Symfony\Component\Validator\Constraint;
use Symfony\Component\Validator\Constraints as Assert;

/**
Expand Down Expand Up @@ -42,9 +43,9 @@ public function __construct(Reader $annotationsReader)
public function updateProperty($reflection, OA\Property $property): void
{
if ($reflection instanceof \ReflectionProperty) {
$annotations = $this->annotationsReader->getPropertyAnnotations($reflection);
$annotations = $this->getPropertyAnnotations($reflection);
} else {
$annotations = $this->annotationsReader->getMethodAnnotations($reflection);
$annotations = $this->getMethodAnnotations($reflection);
}

foreach ($annotations as $annotation) {
Expand Down Expand Up @@ -150,4 +151,26 @@ private function applyEnumFromChoiceConstraint(OA\Schema $property, Assert\Choic

$setEnumOnThis->enum = array_values($enumValues);
}

private function getPropertyAnnotations(\ReflectionProperty $reflection): \Traversable
{
if (\PHP_VERSION_ID >= 80000) {
foreach ($reflection->getAttributes(Constraint::class, \ReflectionAttribute::IS_INSTANCEOF) as $attribute) {
yield $attribute->newInstance();
}
}

yield from $this->annotationsReader->getPropertyAnnotations($reflection);
}

private function getMethodAnnotations(\ReflectionMethod $reflection): \Traversable
{
if (\PHP_VERSION_ID >= 80000) {
foreach ($reflection->getAttributes(Constraint::class, \ReflectionAttribute::IS_INSTANCEOF) as $attribute) {
yield $attribute->newInstance();
}
}

yield from $this->annotationsReader->getMethodAnnotations($reflection);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -47,24 +47,16 @@ public function testUpdatePropertyFix1283()
$this->assertEquals($schema->required, ['property1', 'property2']);
}

public function testOptionalProperty()
/**
* @param object $entity
* @dataProvider provideOptionalProperty
*/
public function testOptionalProperty($entity)
{
if (!\property_exists(Assert\NotBlank::class, 'allowNull')) {
$this->markTestSkipped('NotBlank::allowNull was added in symfony/validator 4.3.');
}

$entity = new class() {
/**
* @Assert\NotBlank(allowNull = true)
* @Assert\Length(min = 1)
*/
private $property1;
/**
* @Assert\NotBlank()
*/
private $property2;
};

$schema = new OA\Schema([]);
$schema->merge([new OA\Property(['property' => 'property1'])]);
$schema->merge([new OA\Property(['property' => 'property2'])]);
Expand All @@ -79,21 +71,37 @@ public function testOptionalProperty()
$this->assertEquals($schema->required, ['property2']);
}

public function testAssertChoiceResultsInNumericArray()
public function provideOptionalProperty(): iterable
{
define('TEST_ASSERT_CHOICE_STATUSES', [
1 => 'active',
2 => 'blocked',
]);

$entity = new class() {
yield 'Annotations' => [new class() {
/**
* @Assert\NotBlank(allowNull = true)
* @Assert\Length(min = 1)
* @Assert\Choice(choices=TEST_ASSERT_CHOICE_STATUSES)
*/
private $property1;
};
/**
* @Assert\NotBlank()
*/
private $property2;
}];

if (\PHP_VERSION_ID >= 80000) {
yield 'Attributes' => [new class() {
#[Assert\NotBlank(allowNull: true)]
#[Assert\Length(min: 1)]
private $property1;
#[Assert\NotBlank]
private $property2;
}];
}
}

/**
* @param object $entity
* @dataProvider provideAssertChoiceResultsInNumericArray
*/
public function testAssertChoiceResultsInNumericArray($entity)
{
$schema = new OA\Schema([]);
$schema->merge([new OA\Property(['property' => 'property1'])]);

Expand All @@ -106,15 +114,36 @@ public function testAssertChoiceResultsInNumericArray()
$this->assertEquals($schema->properties[0]->enum, ['active', 'blocked']);
}

public function testMultipleChoiceConstraintsApplyEnumToItems()
public function provideAssertChoiceResultsInNumericArray(): iterable
{
$entity = new class() {
define('TEST_ASSERT_CHOICE_STATUSES', [
1 => 'active',
2 => 'blocked',
]);

yield 'Annotations' => [new class() {
/**
* @Assert\Choice(choices={"one", "two"}, multiple=true)
* @Assert\Length(min = 1)
* @Assert\Choice(choices=TEST_ASSERT_CHOICE_STATUSES)
*/
private $property1;
};
}];

if (\PHP_VERSION_ID >= 80000) {
yield 'Attributes' => [new class() {
#[Assert\Length(min: 1)]
#[Assert\Choice(choices: TEST_ASSERT_CHOICE_STATUSES)]
private $property1;
}];
}
}

/**
* @param object $entity
* @dataProvider provideMultipleChoiceConstraintsApplyEnumToItems
*/
public function testMultipleChoiceConstraintsApplyEnumToItems($entity)
{
$schema = new OA\Schema([]);
$schema->merge([new OA\Property(['property' => 'property1'])]);

Expand All @@ -127,18 +156,30 @@ public function testMultipleChoiceConstraintsApplyEnumToItems()
$this->assertEquals($schema->properties[0]->items->enum, ['one', 'two']);
}

/**
* @group https://github.com/nelmio/NelmioApiDocBundle/issues/1780
*/
public function testLengthConstraintDoesNotSetMaxLengthIfMaxIsNotSet()
public function provideMultipleChoiceConstraintsApplyEnumToItems(): iterable
{
$entity = new class() {
yield 'Annotations' => [new class() {
/**
* @Assert\Length(min = 1)
* @Assert\Choice(choices={"one", "two"}, multiple=true)
*/
private $property1;
};
}];

if (\PHP_VERSION_ID >= 80000) {
yield 'Attributes' => [new class() {
#[Assert\Choice(choices: ['one', 'two'], multiple: true)]
private $property1;
}];
}
}

/**
* @param object $entity
* @group https://github.com/nelmio/NelmioApiDocBundle/issues/1780
* @dataProvider provideLengthConstraintDoesNotSetMaxLengthIfMaxIsNotSet
*/
public function testLengthConstraintDoesNotSetMaxLengthIfMaxIsNotSet($entity)
{
$schema = new OA\Schema([]);
$schema->merge([new OA\Property(['property' => 'property1'])]);

Expand All @@ -151,18 +192,30 @@ public function testLengthConstraintDoesNotSetMaxLengthIfMaxIsNotSet()
$this->assertSame(1, $schema->properties[0]->minLength);
}

/**
* @group https://github.com/nelmio/NelmioApiDocBundle/issues/1780
*/
public function testLengthConstraintDoesNotSetMinLengthIfMinIsNotSet()
public function provideLengthConstraintDoesNotSetMaxLengthIfMaxIsNotSet(): iterable
{
$entity = new class() {
yield 'Annotations' => [new class() {
/**
* @Assert\Length(max = 100)
* @Assert\Length(min = 1)
*/
private $property1;
};
}];

if (\PHP_VERSION_ID >= 80000) {
yield 'Attributes' => [new class() {
#[Assert\Length(min: 1)]
private $property1;
}];
}
}

/**
* @param object $entity
* @group https://github.com/nelmio/NelmioApiDocBundle/issues/1780
* @dataProvider provideLengthConstraintDoesNotSetMinLengthIfMinIsNotSet
*/
public function testLengthConstraintDoesNotSetMinLengthIfMinIsNotSet($entity)
{
$schema = new OA\Schema([]);
$schema->merge([new OA\Property(['property' => 'property1'])]);

Expand All @@ -174,4 +227,21 @@ public function testLengthConstraintDoesNotSetMinLengthIfMinIsNotSet()
$this->assertSame(OA\UNDEFINED, $schema->properties[0]->minLength);
$this->assertSame(100, $schema->properties[0]->maxLength);
}

public function provideLengthConstraintDoesNotSetMinLengthIfMinIsNotSet(): iterable
{
yield 'Annotations' => [new class() {
/**
* @Assert\Length(max = 100)
*/
private $property1;
}];

if (\PHP_VERSION_ID >= 80000) {
yield 'Attributes' => [new class() {
#[Assert\Length(max: 100)]
private $property1;
}];
}
}
}

0 comments on commit 7d124d6

Please sign in to comment.