mirror of
https://github.com/retailcrm/NelmioApiDocBundle.git
synced 2025-03-11 18:16:13 +03:00
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:
parent
187f55941c
commit
2df454c0c3
@ -44,22 +44,33 @@ class SymfonyConstraintAnnotationReader
|
|||||||
*/
|
*/
|
||||||
public function updateProperty($reflection, OA\Property $property): void
|
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) {
|
if ($annotation instanceof Assert\NotBlank || $annotation instanceof Assert\NotNull) {
|
||||||
// To support symfony/validator < 4.3
|
// To support symfony/validator < 4.3
|
||||||
if ($annotation instanceof Assert\NotBlank && \property_exists($annotation, 'allowNull') && $annotation->allowNull) {
|
if ($annotation instanceof Assert\NotBlank && \property_exists($annotation, 'allowNull') && $annotation->allowNull) {
|
||||||
// The field is optional
|
// The field is optional
|
||||||
continue;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// The field is required
|
// The field is required
|
||||||
if (null === $this->schema) {
|
if (null === $this->schema) {
|
||||||
continue;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
$propertyName = $this->getSchemaPropertyName($property);
|
$propertyName = $this->getSchemaPropertyName($property);
|
||||||
if (null === $propertyName) {
|
if (null === $propertyName) {
|
||||||
continue;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
$existingRequiredFields = OA\UNDEFINED !== $this->schema->required ? $this->schema->required : [];
|
$existingRequiredFields = OA\UNDEFINED !== $this->schema->required ? $this->schema->required : [];
|
||||||
|
@ -11,6 +11,7 @@
|
|||||||
|
|
||||||
namespace Nelmio\ApiDocBundle\Tests\Functional\Entity;
|
namespace Nelmio\ApiDocBundle\Tests\Functional\Entity;
|
||||||
|
|
||||||
|
use Nelmio\ApiDocBundle\Tests\ModelDescriber\Annotations\Fixture as CustomAssert;
|
||||||
use Symfony\Component\Validator\Constraints as Assert;
|
use Symfony\Component\Validator\Constraints as Assert;
|
||||||
|
|
||||||
class SymfonyConstraints
|
class SymfonyConstraints
|
||||||
@ -109,6 +110,18 @@ class SymfonyConstraints
|
|||||||
*/
|
*/
|
||||||
private $propertyLessThanOrEqual;
|
private $propertyLessThanOrEqual;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var int
|
||||||
|
*
|
||||||
|
* @CustomAssert\CompoundValidationRule()
|
||||||
|
*/
|
||||||
|
private $propertyWithCompoundValidationRule;
|
||||||
|
|
||||||
|
public function setPropertyWithCompoundValidationRule(int $propertyWithCompoundValidationRule): void
|
||||||
|
{
|
||||||
|
$this->propertyWithCompoundValidationRule = $propertyWithCompoundValidationRule;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @Assert\Count(min="0", max="10")
|
* @Assert\Count(min="0", max="10")
|
||||||
*/
|
*/
|
||||||
|
@ -12,6 +12,7 @@
|
|||||||
namespace Nelmio\ApiDocBundle\Tests\Functional;
|
namespace Nelmio\ApiDocBundle\Tests\Functional;
|
||||||
|
|
||||||
use Nelmio\ApiDocBundle\OpenApiPhp\Util;
|
use Nelmio\ApiDocBundle\OpenApiPhp\Util;
|
||||||
|
use Nelmio\ApiDocBundle\Tests\Helper;
|
||||||
use OpenApi\Annotations as OA;
|
use OpenApi\Annotations as OA;
|
||||||
use Symfony\Component\Serializer\Annotation\SerializedName;
|
use Symfony\Component\Serializer\Annotation\SerializedName;
|
||||||
|
|
||||||
@ -355,7 +356,7 @@ class FunctionalTest extends WebTestCase
|
|||||||
|
|
||||||
public function testSymfonyConstraintDocumentation()
|
public function testSymfonyConstraintDocumentation()
|
||||||
{
|
{
|
||||||
$this->assertEquals([
|
$expected = [
|
||||||
'required' => [
|
'required' => [
|
||||||
'propertyNotBlank',
|
'propertyNotBlank',
|
||||||
'propertyNotNull',
|
'propertyNotNull',
|
||||||
@ -419,10 +420,26 @@ class FunctionalTest extends WebTestCase
|
|||||||
'type' => 'integer',
|
'type' => 'integer',
|
||||||
'maximum' => 23,
|
'maximum' => 23,
|
||||||
],
|
],
|
||||||
|
'propertyWithCompoundValidationRule' => [
|
||||||
|
'type' => 'integer',
|
||||||
|
],
|
||||||
],
|
],
|
||||||
'type' => 'object',
|
'type' => 'object',
|
||||||
'schema' => 'SymfonyConstraints',
|
'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()
|
public function testConfigReference()
|
||||||
|
17
Tests/Helper.php
Normal file
17
Tests/Helper.php
Normal 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', '>=');
|
||||||
|
}
|
||||||
|
}
|
10
Tests/ModelDescriber/Annotations/Fixture/CompoundStub.php
Normal file
10
Tests/ModelDescriber/Annotations/Fixture/CompoundStub.php
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace Nelmio\ApiDocBundle\Tests\ModelDescriber\Annotations\Fixture;
|
||||||
|
|
||||||
|
class CompoundStub
|
||||||
|
{
|
||||||
|
public $constraints = [];
|
||||||
|
}
|
@ -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),
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
@ -13,6 +13,8 @@ namespace Nelmio\ApiDocBundle\Tests\ModelDescriber\Annotations;
|
|||||||
|
|
||||||
use Doctrine\Common\Annotations\AnnotationReader;
|
use Doctrine\Common\Annotations\AnnotationReader;
|
||||||
use Nelmio\ApiDocBundle\ModelDescriber\Annotations\SymfonyConstraintAnnotationReader;
|
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 OpenApi\Annotations as OA;
|
||||||
use PHPUnit\Framework\TestCase;
|
use PHPUnit\Framework\TestCase;
|
||||||
use Symfony\Component\Validator\Constraints as Assert;
|
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
|
* @param object $entity
|
||||||
* @group https://github.com/nelmio/NelmioApiDocBundle/issues/1821
|
* @group https://github.com/nelmio/NelmioApiDocBundle/issues/1821
|
||||||
|
@ -52,7 +52,8 @@
|
|||||||
"friendsofsymfony/rest-bundle": "^2.8|^3.0",
|
"friendsofsymfony/rest-bundle": "^2.8|^3.0",
|
||||||
"willdurand/hateoas-bundle": "^1.0|^2.0",
|
"willdurand/hateoas-bundle": "^1.0|^2.0",
|
||||||
"jms/serializer-bundle": "^2.3|^3.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": {
|
"suggest": {
|
||||||
"api-platform/core": "For using an API oriented framework.",
|
"api-platform/core": "For using an API oriented framework.",
|
||||||
|
Loading…
x
Reference in New Issue
Block a user