Compound validation rule support (#1818)

* Compound validation rule support

* Compound validation rule support

* Compound validation rule support

* Compound validation rule support

* Remove duplicated method

* error during merge

* wrong variable name

* Simplify PR

* Fix CS

* Use same indentation as before

Co-authored-by: Guilhem Niot <guilhem@gniot.fr>
Co-authored-by: Guilhem Niot <guilhem.niot@gmail.com>
This commit is contained in:
Alexander Melihov 2021-06-16 10:59:06 +03:00 committed by GitHub
parent 187f55941c
commit 2df454c0c3
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 139 additions and 7 deletions

View File

@ -44,22 +44,33 @@ class SymfonyConstraintAnnotationReader
*/
public function updateProperty($reflection, OA\Property $property): void
{
foreach ($this->getAnnotations($reflection) as $annotation) {
foreach ($this->getAnnotations($reflection) as $outerAnnotation) {
$innerAnnotations = $outerAnnotation instanceof Assert\Compound
? $outerAnnotation->constraints
: [$outerAnnotation];
$this->processPropertyAnnotations($reflection, $property, $innerAnnotations);
}
}
private function processPropertyAnnotations($reflection, OA\Property $property, $annotations)
{
foreach ($annotations as $annotation) {
if ($annotation instanceof Assert\NotBlank || $annotation instanceof Assert\NotNull) {
// To support symfony/validator < 4.3
if ($annotation instanceof Assert\NotBlank && \property_exists($annotation, 'allowNull') && $annotation->allowNull) {
// The field is optional
continue;
return;
}
// The field is required
if (null === $this->schema) {
continue;
return;
}
$propertyName = $this->getSchemaPropertyName($property);
if (null === $propertyName) {
continue;
return;
}
$existingRequiredFields = OA\UNDEFINED !== $this->schema->required ? $this->schema->required : [];

View File

@ -11,6 +11,7 @@
namespace Nelmio\ApiDocBundle\Tests\Functional\Entity;
use Nelmio\ApiDocBundle\Tests\ModelDescriber\Annotations\Fixture as CustomAssert;
use Symfony\Component\Validator\Constraints as Assert;
class SymfonyConstraints
@ -109,6 +110,18 @@ class SymfonyConstraints
*/
private $propertyLessThanOrEqual;
/**
* @var int
*
* @CustomAssert\CompoundValidationRule()
*/
private $propertyWithCompoundValidationRule;
public function setPropertyWithCompoundValidationRule(int $propertyWithCompoundValidationRule): void
{
$this->propertyWithCompoundValidationRule = $propertyWithCompoundValidationRule;
}
/**
* @Assert\Count(min="0", max="10")
*/

View File

@ -12,6 +12,7 @@
namespace Nelmio\ApiDocBundle\Tests\Functional;
use Nelmio\ApiDocBundle\OpenApiPhp\Util;
use Nelmio\ApiDocBundle\Tests\Helper;
use OpenApi\Annotations as OA;
use Symfony\Component\Serializer\Annotation\SerializedName;
@ -355,7 +356,7 @@ class FunctionalTest extends WebTestCase
public function testSymfonyConstraintDocumentation()
{
$this->assertEquals([
$expected = [
'required' => [
'propertyNotBlank',
'propertyNotNull',
@ -419,10 +420,26 @@ class FunctionalTest extends WebTestCase
'type' => 'integer',
'maximum' => 23,
],
'propertyWithCompoundValidationRule' => [
'type' => 'integer',
],
],
'type' => 'object',
'schema' => 'SymfonyConstraints',
], json_decode($this->getModel('SymfonyConstraints')->toJson(), true));
];
if (Helper::isCompoundValidatorConstraintSupported()) {
$expected['required'][] = 'propertyWithCompoundValidationRule';
$expected['properties']['propertyWithCompoundValidationRule'] = [
'type' => 'integer',
'maximum' => 5,
'exclusiveMaximum' => true,
'minimum' => 0,
'exclusiveMinimum' => true,
];
}
$this->assertEquals($expected, json_decode($this->getModel('SymfonyConstraints')->toJson(), true));
}
public function testConfigReference()

17
Tests/Helper.php Normal file
View File

@ -0,0 +1,17 @@
<?php
declare(strict_types=1);
namespace Nelmio\ApiDocBundle\Tests;
use PackageVersions\Versions;
final class Helper
{
public static function isCompoundValidatorConstraintSupported(): bool
{
$validatorVersion = Versions::getVersion('symfony/validator');
return version_compare($validatorVersion, 'v5.1', '>=');
}
}

View File

@ -0,0 +1,10 @@
<?php
declare(strict_types=1);
namespace Nelmio\ApiDocBundle\Tests\ModelDescriber\Annotations\Fixture;
class CompoundStub
{
public $constraints = [];
}

View File

@ -0,0 +1,28 @@
<?php
declare(strict_types=1);
namespace Nelmio\ApiDocBundle\Tests\ModelDescriber\Annotations\Fixture;
use Symfony\Component\Validator\Constraints as Assert;
use Symfony\Component\Validator\Constraints\Compound;
if (!class_exists(Compound::class)) {
class_alias(CompoundStub::class, Compound::class);
}
/**
* @Annotation
*/
final class CompoundValidationRule extends Compound
{
protected function getConstraints(array $options): array
{
return [
new Assert\Type('numeric'),
new Assert\NotBlank(),
new Assert\Positive(),
new Assert\LessThan(5),
];
}
}

View File

@ -13,6 +13,8 @@ namespace Nelmio\ApiDocBundle\Tests\ModelDescriber\Annotations;
use Doctrine\Common\Annotations\AnnotationReader;
use Nelmio\ApiDocBundle\ModelDescriber\Annotations\SymfonyConstraintAnnotationReader;
use Nelmio\ApiDocBundle\Tests\Helper;
use Nelmio\ApiDocBundle\Tests\ModelDescriber\Annotations\Fixture as CustomAssert;
use OpenApi\Annotations as OA;
use PHPUnit\Framework\TestCase;
use Symfony\Component\Validator\Constraints as Assert;
@ -245,6 +247,39 @@ class SymfonyConstraintAnnotationReaderTest extends TestCase
}
}
public function testCompoundValidationRules()
{
$entity = new class() {
/**
* @CustomAssert\CompoundValidationRule()
*/
private $property1;
};
$propertyName = 'property1';
$schema = new OA\Schema([]);
$schema->merge([new OA\Property(['property' => $propertyName])]);
$symfonyConstraintAnnotationReader = new SymfonyConstraintAnnotationReader(new AnnotationReader());
$symfonyConstraintAnnotationReader->setSchema($schema);
$symfonyConstraintAnnotationReader->updateProperty(new \ReflectionProperty($entity, $propertyName), $schema->properties[0]);
if (Helper::isCompoundValidatorConstraintSupported()) {
$this->assertSame([$propertyName], $schema->required);
$this->assertSame(0, $schema->properties[0]->minimum);
$this->assertTrue($schema->properties[0]->exclusiveMinimum);
$this->assertSame(5, $schema->properties[0]->maximum);
$this->assertTrue($schema->properties[0]->exclusiveMaximum);
} else {
$this->assertSame(OA\UNDEFINED, $schema->required);
$this->assertSame(OA\UNDEFINED, $schema->properties[0]->minimum);
$this->assertSame(OA\UNDEFINED, $schema->properties[0]->exclusiveMinimum);
$this->assertSame(OA\UNDEFINED, $schema->properties[0]->maximum);
$this->assertSame(OA\UNDEFINED, $schema->properties[0]->exclusiveMaximum);
}
}
/**
* @param object $entity
* @group https://github.com/nelmio/NelmioApiDocBundle/issues/1821

View File

@ -52,7 +52,8 @@
"friendsofsymfony/rest-bundle": "^2.8|^3.0",
"willdurand/hateoas-bundle": "^1.0|^2.0",
"jms/serializer-bundle": "^2.3|^3.0",
"jms/serializer": "^1.14|^3.0"
"jms/serializer": "^1.14|^3.0",
"composer/package-versions-deprecated": "1.11.99.1"
},
"suggest": {
"api-platform/core": "For using an API oriented framework.",