From 5f1645db40e2150817468fc9687ac69231412ec6 Mon Sep 17 00:00:00 2001 From: Christopher Davis Date: Tue, 25 May 2021 06:39:21 -0500 Subject: [PATCH] Check min/max On Range Constraints Before Use Previously it was possible to set only one of the min or max values and get a schema like: "property": { "type": "integer", "minimum": 1, "maximum": 0 } Also possible that `Range` would be used with {min,max}PropertyPath and you'd get a schema with both minimum and max set to zero. With the checks in place, that's no longer the case. --- .../SymfonyConstraintAnnotationReader.php | 8 ++- .../SymfonyConstraintAnnotationReaderTest.php | 72 +++++++++++++++++++ 2 files changed, 78 insertions(+), 2 deletions(-) diff --git a/ModelDescriber/Annotations/SymfonyConstraintAnnotationReader.php b/ModelDescriber/Annotations/SymfonyConstraintAnnotationReader.php index aad50c6..421ddab 100644 --- a/ModelDescriber/Annotations/SymfonyConstraintAnnotationReader.php +++ b/ModelDescriber/Annotations/SymfonyConstraintAnnotationReader.php @@ -85,8 +85,12 @@ class SymfonyConstraintAnnotationReader } elseif ($annotation instanceof Assert\Choice) { $this->applyEnumFromChoiceConstraint($property, $annotation, $reflection); } elseif ($annotation instanceof Assert\Range) { - $property->minimum = (int) $annotation->min; - $property->maximum = (int) $annotation->max; + if (isset($annotation->min)) { + $property->minimum = (int) $annotation->min; + } + if (isset($annotation->max)) { + $property->maximum = (int) $annotation->max; + } } elseif ($annotation instanceof Assert\LessThan) { $property->exclusiveMaximum = true; $property->maximum = (int) $annotation->value; diff --git a/Tests/ModelDescriber/Annotations/SymfonyConstraintAnnotationReaderTest.php b/Tests/ModelDescriber/Annotations/SymfonyConstraintAnnotationReaderTest.php index 8e69ee8..d3c96f3 100644 --- a/Tests/ModelDescriber/Annotations/SymfonyConstraintAnnotationReaderTest.php +++ b/Tests/ModelDescriber/Annotations/SymfonyConstraintAnnotationReaderTest.php @@ -316,4 +316,76 @@ class SymfonyConstraintAnnotationReaderTest extends TestCase }]; } } + + /** + * @param object $entity + * @group https://github.com/nelmio/NelmioApiDocBundle/issues/1822 + * @dataProvider provideRangeConstraintDoesNotSetMaximumIfMaxIsNotSet + */ + public function testRangeConstraintDoesNotSetMaximumIfMaxIsNotSet($entity) + { + $schema = new OA\Schema([]); + $schema->merge([new OA\Property(['property' => 'property1'])]); + + $symfonyConstraintAnnotationReader = new SymfonyConstraintAnnotationReader(new AnnotationReader()); + $symfonyConstraintAnnotationReader->setSchema($schema); + + $symfonyConstraintAnnotationReader->updateProperty(new \ReflectionProperty($entity, 'property1'), $schema->properties[0]); + + $this->assertSame(OA\UNDEFINED, $schema->properties[0]->maximum); + $this->assertSame(10, $schema->properties[0]->minimum); + } + + public function provideRangeConstraintDoesNotSetMaximumIfMaxIsNotSet(): iterable + { + yield 'Annotations' => [new class() { + /** + * @Assert\Range(min = 10) + */ + private $property1; + }]; + + if (\PHP_VERSION_ID >= 80000) { + yield 'Attributes' => [new class() { + #[Assert\Range(min: 10)] + private $property1; + }]; + } + } + + /** + * @param object $entity + * @group https://github.com/nelmio/NelmioApiDocBundle/issues/1822 + * @dataProvider provideRangeConstraintDoesNotSetMinimumIfMinIsNotSet + */ + public function testRangeConstraintDoesNotSetMinimumIfMinIsNotSet($entity) + { + $schema = new OA\Schema([]); + $schema->merge([new OA\Property(['property' => 'property1'])]); + + $symfonyConstraintAnnotationReader = new SymfonyConstraintAnnotationReader(new AnnotationReader()); + $symfonyConstraintAnnotationReader->setSchema($schema); + + $symfonyConstraintAnnotationReader->updateProperty(new \ReflectionProperty($entity, 'property1'), $schema->properties[0]); + + $this->assertSame(OA\UNDEFINED, $schema->properties[0]->minimum); + $this->assertSame(10, $schema->properties[0]->maximum); + } + + public function provideRangeConstraintDoesNotSetMinimumIfMinIsNotSet(): iterable + { + yield 'Annotations' => [new class() { + /** + * @Assert\Range(max = 10) + */ + private $property1; + }]; + + if (\PHP_VERSION_ID >= 80000) { + yield 'Attributes' => [new class() { + #[Assert\Range(max: 10)] + private $property1; + }]; + } + } }