mirror of
https://github.com/retailcrm/NelmioApiDocBundle.git
synced 2025-02-02 23:59:26 +03:00
Apply enum
from Choice Constraints to Items When Choice is Multiple (#1784)
* Apply `enum` from Choice Constraints to Items When Choice is Multiple Otherwise JSON schema like this is generated: ``` "property": { "type": "array", "enum": ["one", "two", "three"], "items": { "type": "string" } } ``` With this change, however, this schema is generated: ``` "property": { "type": "array", "items": { "type": "string", "enum": ["one", "two", "three"] } } ``` A possible downside here is that the symfony constraint stuff happens before types are figured out from PHPDoc. So it's _possible_ to end up with something that won't validated. Take something like this: ``` /** * @Assert\Choice(multiple=true, choices={"..."}) * @var string */ ``` This would generate: ``` "property": { "type": "string", "items": { "enum": ["..."] } } ``` * Fix CS * cs * more cs * fix tests Co-authored-by: Guilhem Niot <guilhem@gniot.fr>
This commit is contained in:
parent
ceda09fe85
commit
883d7b6c89
@ -12,6 +12,7 @@
|
|||||||
namespace Nelmio\ApiDocBundle\ModelDescriber\Annotations;
|
namespace Nelmio\ApiDocBundle\ModelDescriber\Annotations;
|
||||||
|
|
||||||
use Doctrine\Common\Annotations\Reader;
|
use Doctrine\Common\Annotations\Reader;
|
||||||
|
use Nelmio\ApiDocBundle\OpenApiPhp\Util;
|
||||||
use OpenApi\Annotations as OA;
|
use OpenApi\Annotations as OA;
|
||||||
use Symfony\Component\Validator\Constraints as Assert;
|
use Symfony\Component\Validator\Constraints as Assert;
|
||||||
|
|
||||||
@ -81,8 +82,7 @@ class SymfonyConstraintAnnotationReader
|
|||||||
$property->minItems = (int) $annotation->min;
|
$property->minItems = (int) $annotation->min;
|
||||||
$property->maxItems = (int) $annotation->max;
|
$property->maxItems = (int) $annotation->max;
|
||||||
} elseif ($annotation instanceof Assert\Choice) {
|
} elseif ($annotation instanceof Assert\Choice) {
|
||||||
$values = $annotation->callback ? call_user_func(is_array($annotation->callback) ? $annotation->callback : [$reflection->class, $annotation->callback]) : $annotation->choices;
|
$this->applyEnumFromChoiceConstraint($property, $annotation, $reflection);
|
||||||
$property->enum = array_values($values);
|
|
||||||
} elseif ($annotation instanceof Assert\Range) {
|
} elseif ($annotation instanceof Assert\Range) {
|
||||||
$property->minimum = (int) $annotation->min;
|
$property->minimum = (int) $annotation->min;
|
||||||
$property->maximum = (int) $annotation->max;
|
$property->maximum = (int) $annotation->max;
|
||||||
@ -131,4 +131,23 @@ class SymfonyConstraintAnnotationReader
|
|||||||
$property->pattern = $newPattern;
|
$property->pattern = $newPattern;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var ReflectionProperty|ReflectionClass
|
||||||
|
*/
|
||||||
|
private function applyEnumFromChoiceConstraint(OA\Schema $property, Assert\Choice $choice, $reflection): void
|
||||||
|
{
|
||||||
|
if ($choice->callback) {
|
||||||
|
$enumValues = call_user_func(is_array($choice->callback) ? $choice->callback : [$reflection->class, $choice->callback]);
|
||||||
|
} else {
|
||||||
|
$enumValues = $choice->choices;
|
||||||
|
}
|
||||||
|
|
||||||
|
$setEnumOnThis = $property;
|
||||||
|
if ($choice->multiple) {
|
||||||
|
$setEnumOnThis = Util::getChild($property, OA\Items::class);
|
||||||
|
}
|
||||||
|
|
||||||
|
$setEnumOnThis->enum = array_values($enumValues);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -71,6 +71,13 @@ class SymfonyConstraints
|
|||||||
*/
|
*/
|
||||||
private $propertyChoiceWithCallbackWithoutClass;
|
private $propertyChoiceWithCallbackWithoutClass;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var string[]
|
||||||
|
*
|
||||||
|
* @Assert\Choice(multiple=true, choices={"choice1", "choice2"})
|
||||||
|
*/
|
||||||
|
private $propertyChoiceWithMultiple;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @var int
|
* @var int
|
||||||
*
|
*
|
||||||
@ -145,6 +152,11 @@ class SymfonyConstraints
|
|||||||
$this->propertyChoiceWithCallbackWithoutClass = $propertyChoiceWithCallbackWithoutClass;
|
$this->propertyChoiceWithCallbackWithoutClass = $propertyChoiceWithCallbackWithoutClass;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function setPropertyChoiceWithMultiple(array $propertyChoiceWithMultiple): void
|
||||||
|
{
|
||||||
|
$this->propertyChoiceWithMultiple = $propertyChoiceWithMultiple;
|
||||||
|
}
|
||||||
|
|
||||||
public function setPropertyExpression(int $propertyExpression): void
|
public function setPropertyExpression(int $propertyExpression): void
|
||||||
{
|
{
|
||||||
$this->propertyExpression = $propertyExpression;
|
$this->propertyExpression = $propertyExpression;
|
||||||
|
@ -395,6 +395,13 @@ class FunctionalTest extends WebTestCase
|
|||||||
'type' => 'integer',
|
'type' => 'integer',
|
||||||
'enum' => ['choice1', 'choice2'],
|
'enum' => ['choice1', 'choice2'],
|
||||||
],
|
],
|
||||||
|
'propertyChoiceWithMultiple' => [
|
||||||
|
'type' => 'array',
|
||||||
|
'items' => [
|
||||||
|
'type' => 'string',
|
||||||
|
'enum' => ['choice1', 'choice2'],
|
||||||
|
],
|
||||||
|
],
|
||||||
'propertyExpression' => [
|
'propertyExpression' => [
|
||||||
'type' => 'integer',
|
'type' => 'integer',
|
||||||
],
|
],
|
||||||
|
@ -106,6 +106,27 @@ class SymfonyConstraintAnnotationReaderTest extends TestCase
|
|||||||
$this->assertEquals($schema->properties[0]->enum, ['active', 'blocked']);
|
$this->assertEquals($schema->properties[0]->enum, ['active', 'blocked']);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function testMultipleChoiceConstraintsApplyEnumToItems()
|
||||||
|
{
|
||||||
|
$entity = new class() {
|
||||||
|
/**
|
||||||
|
* @Assert\Choice(choices={"one", "two"}, multiple=true)
|
||||||
|
*/
|
||||||
|
private $property1;
|
||||||
|
};
|
||||||
|
|
||||||
|
$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->assertInstanceOf(OA\Items::class, $schema->properties[0]->items);
|
||||||
|
$this->assertEquals($schema->properties[0]->items->enum, ['one', 'two']);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @group https://github.com/nelmio/NelmioApiDocBundle/issues/1780
|
* @group https://github.com/nelmio/NelmioApiDocBundle/issues/1780
|
||||||
*/
|
*/
|
||||||
|
Loading…
x
Reference in New Issue
Block a user