mirror of
https://github.com/retailcrm/NelmioApiDocBundle.git
synced 2025-02-09 02:59:27 +03:00
support swagger property annotation to extend description properties of model (#1125)
* support swagger property annotation to descripe properties of model * support swagger property annotation to descripe properties of model * fix issues from PR review * rename method * remove redundant annotations and revert changes into composer.json * fix issues from PR comments * use symfony 3 for default tests * revert chages * use symfony 3 for default tests * revert changes in travis config
This commit is contained in:
parent
8a023a1897
commit
01f691c456
@ -99,7 +99,11 @@ final class NelmioApiDocExtension extends Extension implements PrependExtensionI
|
||||
if ($config['models']['use_jms']) {
|
||||
$container->register('nelmio_api_doc.model_describers.jms', JMSModelDescriber::class)
|
||||
->setPublic(false)
|
||||
->setArguments([new Reference('jms_serializer.metadata_factory'), new Reference('jms_serializer.naming_strategy')])
|
||||
->setArguments([
|
||||
new Reference('jms_serializer.metadata_factory'),
|
||||
new Reference('jms_serializer.naming_strategy'),
|
||||
new Reference('nelmio_api_doc.model_describers.swagger_property_annotation_reader'),
|
||||
])
|
||||
->addTag('nelmio_api_doc.model_describer', ['priority' => 50]);
|
||||
}
|
||||
|
||||
|
@ -1,35 +0,0 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the NelmioApiDocBundle package.
|
||||
*
|
||||
* (c) Nelmio
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Nelmio\ApiDocBundle\ModelDescriber;
|
||||
|
||||
use EXSyst\Component\Swagger\Schema;
|
||||
use Nelmio\ApiDocBundle\Describer\ModelRegistryAwareInterface;
|
||||
use Nelmio\ApiDocBundle\Describer\ModelRegistryAwareTrait;
|
||||
use Nelmio\ApiDocBundle\Model\Model;
|
||||
|
||||
class CollectionModelDescriber implements ModelDescriberInterface, ModelRegistryAwareInterface
|
||||
{
|
||||
use ModelRegistryAwareTrait;
|
||||
|
||||
public function describe(Model $model, Schema $schema)
|
||||
{
|
||||
$schema->setType('array');
|
||||
$schema->getItems()->setRef(
|
||||
$this->modelRegistry->register(new Model($model->getType()->getCollectionValueType(), $model->getGroups()))
|
||||
);
|
||||
}
|
||||
|
||||
public function supports(Model $model): bool
|
||||
{
|
||||
return $model->getType()->isCollection() && null !== $model->getType()->getCollectionValueType();
|
||||
}
|
||||
}
|
@ -1,29 +0,0 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the NelmioApiDocBundle package.
|
||||
*
|
||||
* (c) Nelmio
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Nelmio\ApiDocBundle\ModelDescriber;
|
||||
|
||||
use EXSyst\Component\Swagger\Schema;
|
||||
use Nelmio\ApiDocBundle\Model\Model;
|
||||
|
||||
class DateTimeModelDescriber implements ModelDescriberInterface
|
||||
{
|
||||
public function describe(Model $model, Schema $schema)
|
||||
{
|
||||
$schema->setType('string');
|
||||
$schema->setFormat('date-time');
|
||||
}
|
||||
|
||||
public function supports(Model $model): bool
|
||||
{
|
||||
return 'DateTime' === $model->getType()->getClassName() || 'DateTimeImmutable' === $model->getType()->getClassName();
|
||||
}
|
||||
}
|
@ -30,12 +30,20 @@ class JMSModelDescriber implements ModelDescriberInterface, ModelRegistryAwareIn
|
||||
use ModelRegistryAwareTrait;
|
||||
|
||||
private $factory;
|
||||
|
||||
private $namingStrategy;
|
||||
|
||||
public function __construct(MetadataFactoryInterface $factory, PropertyNamingStrategyInterface $namingStrategy)
|
||||
private $swaggerPropertyAnnotationReader;
|
||||
|
||||
public function __construct(
|
||||
MetadataFactoryInterface $factory,
|
||||
PropertyNamingStrategyInterface $namingStrategy,
|
||||
SwaggerPropertyAnnotationReader $swaggerPropertyAnnotationReader
|
||||
)
|
||||
{
|
||||
$this->factory = $factory;
|
||||
$this->namingStrategy = $namingStrategy;
|
||||
$this->swaggerPropertyAnnotationReader = $swaggerPropertyAnnotationReader;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -73,12 +81,12 @@ class JMSModelDescriber implements ModelDescriberInterface, ModelRegistryAwareIn
|
||||
$type = $item->type['name'];
|
||||
}
|
||||
|
||||
if (in_array($type, array('boolean', 'integer', 'string', 'array'))) {
|
||||
if (in_array($type, ['boolean', 'integer', 'string', 'array'])) {
|
||||
$property->setType($type);
|
||||
} elseif ('double' === $type || 'float' === $type) {
|
||||
} elseif (in_array($type, ['double', 'float'])) {
|
||||
$property->setType('number');
|
||||
$property->setFormat($type);
|
||||
} elseif ('DateTime' === $type || 'DateTimeImmutable' === $type) {
|
||||
} elseif (in_array($type, ['DateTime', 'DateTimeImmutable'])) {
|
||||
$property->setType('string');
|
||||
$property->setFormat('date-time');
|
||||
} else {
|
||||
@ -91,6 +99,9 @@ class JMSModelDescriber implements ModelDescriberInterface, ModelRegistryAwareIn
|
||||
$this->modelRegistry->register(new Model(new Type(Type::BUILTIN_TYPE_OBJECT, false, $type), $model->getGroups()))
|
||||
);
|
||||
}
|
||||
|
||||
// read property options from Swagger Property annotation if it exists
|
||||
$this->swaggerPropertyAnnotationReader->updateWithSwaggerPropertyAnnotation($item->reflection, $property);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -24,9 +24,15 @@ class ObjectModelDescriber implements ModelDescriberInterface, ModelRegistryAwar
|
||||
|
||||
private $propertyInfo;
|
||||
|
||||
public function __construct(PropertyInfoExtractorInterface $propertyInfo)
|
||||
private $swaggerPropertyAnnotationReader;
|
||||
|
||||
public function __construct(
|
||||
PropertyInfoExtractorInterface $propertyInfo,
|
||||
SwaggerPropertyAnnotationReader $swaggerPropertyAnnotationReader
|
||||
)
|
||||
{
|
||||
$this->propertyInfo = $propertyInfo;
|
||||
$this->swaggerPropertyAnnotationReader = $swaggerPropertyAnnotationReader;
|
||||
}
|
||||
|
||||
public function describe(Model $model, Schema $schema)
|
||||
@ -54,9 +60,44 @@ class ObjectModelDescriber implements ModelDescriberInterface, ModelRegistryAwar
|
||||
throw new \LogicException(sprintf('Property %s::$%s defines more than one type.', $class, $propertyName));
|
||||
}
|
||||
|
||||
$properties->get($propertyName)->setRef(
|
||||
$this->modelRegistry->register(new Model($types[0], $model->getGroups()))
|
||||
);
|
||||
$type = $types[0];
|
||||
$property = $properties->get($propertyName);
|
||||
|
||||
if (Type::BUILTIN_TYPE_ARRAY === $type->getBuiltinType()) {
|
||||
$type = $type->getCollectionValueType();
|
||||
$property->setType('array');
|
||||
$property = $property->getItems();
|
||||
}
|
||||
|
||||
if ($type->getBuiltinType() === Type::BUILTIN_TYPE_STRING) {
|
||||
$property->setType('string');
|
||||
} elseif ($type->getBuiltinType() === Type::BUILTIN_TYPE_BOOL) {
|
||||
$property->setType('boolean');
|
||||
} elseif ($type->getBuiltinType() === Type::BUILTIN_TYPE_INT) {
|
||||
$property->setType('integer');
|
||||
} elseif ($type->getBuiltinType() === Type::BUILTIN_TYPE_FLOAT) {
|
||||
$property->setType('number');
|
||||
$property->setFormat('float');
|
||||
} elseif ($type->getBuiltinType() === Type::BUILTIN_TYPE_OBJECT) {
|
||||
if (in_array($type->getClassName(), ['DateTime', 'DateTimeImmutable'])) {
|
||||
$property->setType('string');
|
||||
$property->setFormat('date-time');
|
||||
} else {
|
||||
$property->setRef(
|
||||
$this->modelRegistry->register(new Model($type, $model->getGroups()))
|
||||
);
|
||||
}
|
||||
} else {
|
||||
throw new \Exception(sprintf("Unknown type: %s", $type->getBuiltinType()));
|
||||
}
|
||||
|
||||
// read property options from Swagger Property annotation if it exists
|
||||
if (property_exists($class, $propertyName)) {
|
||||
$this->swaggerPropertyAnnotationReader->updateWithSwaggerPropertyAnnotation(
|
||||
new \ReflectionProperty($class, $propertyName),
|
||||
$property
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1,37 +0,0 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the NelmioApiDocBundle package.
|
||||
*
|
||||
* (c) Nelmio
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Nelmio\ApiDocBundle\ModelDescriber;
|
||||
|
||||
use EXSyst\Component\Swagger\Schema;
|
||||
use Nelmio\ApiDocBundle\Model\Model;
|
||||
use Symfony\Component\PropertyInfo\Type;
|
||||
|
||||
class ScalarModelDescriber implements ModelDescriberInterface
|
||||
{
|
||||
private static $supportedTypes = [
|
||||
Type::BUILTIN_TYPE_INT => 'integer',
|
||||
Type::BUILTIN_TYPE_FLOAT => 'float',
|
||||
Type::BUILTIN_TYPE_STRING => 'string',
|
||||
Type::BUILTIN_TYPE_BOOL => 'boolean',
|
||||
];
|
||||
|
||||
public function describe(Model $model, Schema $schema)
|
||||
{
|
||||
$type = self::$supportedTypes[$model->getType()->getBuiltinType()];
|
||||
$schema->setType($type);
|
||||
}
|
||||
|
||||
public function supports(Model $model): bool
|
||||
{
|
||||
return isset(self::$supportedTypes[$model->getType()->getBuiltinType()]);
|
||||
}
|
||||
}
|
56
ModelDescriber/SwaggerPropertyAnnotationReader.php
Normal file
56
ModelDescriber/SwaggerPropertyAnnotationReader.php
Normal file
@ -0,0 +1,56 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the NelmioApiDocBundle package.
|
||||
*
|
||||
* (c) Nelmio
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Nelmio\ApiDocBundle\ModelDescriber;
|
||||
|
||||
use EXSyst\Component\Swagger\Schema;
|
||||
use EXSyst\Component\Swagger\Items;
|
||||
use Swagger\Annotations\Property as SwgProperty;
|
||||
use Doctrine\Common\Annotations\Reader;
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
class SwaggerPropertyAnnotationReader
|
||||
{
|
||||
private $annotationsReader;
|
||||
|
||||
public function __construct(Reader $annotationsReader)
|
||||
{
|
||||
$this->annotationsReader = $annotationsReader;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param \ReflectionProperty $reflectionProperty
|
||||
* @param Items|Schema $property
|
||||
*/
|
||||
public function updateWithSwaggerPropertyAnnotation(\ReflectionProperty $reflectionProperty, $property)
|
||||
{
|
||||
$swgProperty = $this->annotationsReader->getPropertyAnnotation($reflectionProperty, SwgProperty::class);
|
||||
if ($swgProperty instanceof SwgProperty) {
|
||||
if ($swgProperty->description !== null) {
|
||||
$property->setDescription($swgProperty->description);
|
||||
}
|
||||
if ($swgProperty->type !== null) {
|
||||
$property->setType($swgProperty->type);
|
||||
}
|
||||
if ($swgProperty->readOnly !== null) {
|
||||
$property->setReadOnly($swgProperty->readOnly);
|
||||
}
|
||||
if ($swgProperty->title !== null) {
|
||||
$property->setTitle($swgProperty->title);
|
||||
}
|
||||
if ($swgProperty->example !== null) {
|
||||
$property->setExample((string) $swgProperty->example);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -50,27 +50,16 @@
|
||||
</service>
|
||||
|
||||
<!-- Model Describers -->
|
||||
<service id="nelmio_api_doc.model_describers.collection" class="Nelmio\ApiDocBundle\ModelDescriber\CollectionModelDescriber" public="false">
|
||||
<tag name="nelmio_api_doc.model_describer" priority="10" />
|
||||
<service id="nelmio_api_doc.model_describers.swagger_property_annotation_reader" class="Nelmio\ApiDocBundle\ModelDescriber\SwaggerPropertyAnnotationReader" public="false">
|
||||
<argument type="service" id="annotation_reader" />
|
||||
</service>
|
||||
|
||||
<service id="nelmio_api_doc.model_describers.object" class="Nelmio\ApiDocBundle\ModelDescriber\ObjectModelDescriber" public="false">
|
||||
<argument type="service" id="property_info" />
|
||||
<argument type="service" id="nelmio_api_doc.model_describers.swagger_property_annotation_reader" />
|
||||
|
||||
<tag name="nelmio_api_doc.model_describer" />
|
||||
</service>
|
||||
|
||||
<service id="nelmio_api_doc.model_describers.datetime" class="Nelmio\ApiDocBundle\ModelDescriber\DateTimeModelDescriber" public="false">
|
||||
<tag name="nelmio_api_doc.model_describer" priority="100" />
|
||||
</service>
|
||||
|
||||
<service id="nelmio_api_doc.model_describers.collection" class="Nelmio\ApiDocBundle\ModelDescriber\CollectionModelDescriber" public="false">
|
||||
<tag name="nelmio_api_doc.model_describer" />
|
||||
</service>
|
||||
|
||||
<service id="nelmio_api_doc.model_describers.scalar" class="Nelmio\ApiDocBundle\ModelDescriber\ScalarModelDescriber" public="false">
|
||||
<tag name="nelmio_api_doc.model_describer" />
|
||||
</service>
|
||||
</services>
|
||||
|
||||
</container>
|
||||
|
@ -12,6 +12,7 @@
|
||||
namespace Nelmio\ApiDocBundle\Tests\Functional\Entity;
|
||||
|
||||
use JMS\Serializer\Annotation as Serializer;
|
||||
use Swagger\Annotations as SWG;
|
||||
|
||||
/**
|
||||
* User.
|
||||
@ -23,12 +24,16 @@ class JMSUser
|
||||
/**
|
||||
* @Serializer\Type("integer")
|
||||
* @Serializer\Expose
|
||||
*
|
||||
* @SWG\Property(description = "User id", required = true, readOnly = true, title = "userid", example=1)
|
||||
*/
|
||||
private $id;
|
||||
|
||||
/**
|
||||
* @Serializer\Type("string")
|
||||
* @Serializer\Expose
|
||||
*
|
||||
* @SWG\Property(readOnly = false)
|
||||
*/
|
||||
private $email;
|
||||
|
||||
@ -57,6 +62,15 @@ class JMSUser
|
||||
*/
|
||||
private $friends;
|
||||
|
||||
/**
|
||||
* @Serializer\Type("integer")
|
||||
* @Serializer\Expose
|
||||
* @Serializer\SerializedName("friendsNumber")
|
||||
*
|
||||
* @SWG\Property(type = "string")
|
||||
*/
|
||||
private $friendsNumber;
|
||||
|
||||
/**
|
||||
* @Serializer\Type(User::class)
|
||||
* @Serializer\Expose
|
||||
|
@ -11,11 +11,39 @@
|
||||
|
||||
namespace Nelmio\ApiDocBundle\Tests\Functional\Entity;
|
||||
|
||||
use Swagger\Annotations as SWG;
|
||||
|
||||
/**
|
||||
* @author Guilhem N. <egetick@gmail.com>
|
||||
*/
|
||||
class User
|
||||
{
|
||||
/**
|
||||
* @var integer
|
||||
*
|
||||
* @SWG\Property(description = "User id", required = true, readOnly = true, title = "userid", example=1)
|
||||
*/
|
||||
private $id;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*
|
||||
* @SWG\Property(readOnly = false)
|
||||
*/
|
||||
private $email;
|
||||
|
||||
/**
|
||||
* @var int
|
||||
*
|
||||
* @SWG\Property(type = "string")
|
||||
*/
|
||||
private $friendsNumber;
|
||||
|
||||
/**
|
||||
* @var float
|
||||
*/
|
||||
private $money;
|
||||
|
||||
/**
|
||||
* @var \DateTime
|
||||
*/
|
||||
@ -26,6 +54,38 @@ class User
|
||||
*/
|
||||
private $users;
|
||||
|
||||
/**
|
||||
* @param float $money
|
||||
*/
|
||||
public function setMoney(float $money)
|
||||
{
|
||||
$this->money = $money;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $id
|
||||
*/
|
||||
public function setId(int $id)
|
||||
{
|
||||
$this->id = $id;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $email
|
||||
*/
|
||||
public function setEmail(string $email)
|
||||
{
|
||||
$this->email = $email;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $friendsNumber
|
||||
*/
|
||||
public function setFriendsNumber(int $friendsNumber)
|
||||
{
|
||||
$this->friendsNumber = $friendsNumber;
|
||||
}
|
||||
|
||||
public function setCreatedAt(\DateTime $createAt)
|
||||
{
|
||||
}
|
||||
|
@ -149,31 +149,45 @@ class FunctionalTest extends WebTestCase
|
||||
|
||||
public function testUserModel()
|
||||
{
|
||||
$model = $this->getModel('User');
|
||||
$this->assertEquals('object', $model->getType());
|
||||
$properties = $model->getProperties();
|
||||
$this->assertCount(3, $properties);
|
||||
|
||||
$this->assertTrue($properties->has('users'));
|
||||
$this->assertEquals('#/definitions/User[]', $properties->get('users')->getRef());
|
||||
|
||||
$this->assertTrue($properties->has('dummy'));
|
||||
$this->assertEquals('#/definitions/Dummy2', $properties->get('dummy')->getRef());
|
||||
|
||||
$this->assertTrue($properties->has('createdAt'));
|
||||
$this->assertEquals('#/definitions/DateTime', $properties->get('createdAt')->getRef());
|
||||
|
||||
$model = $this->getModel('DateTime');
|
||||
$this->assertEquals('string', $model->getType());
|
||||
$this->assertEquals('date-time', $model->getFormat());
|
||||
}
|
||||
|
||||
public function testUsersModel()
|
||||
{
|
||||
$model = $this->getModel('User[]');
|
||||
$this->assertEquals('array', $model->getType());
|
||||
|
||||
$this->assertEquals('#/definitions/User', $model->getItems()->getRef());
|
||||
$this->assertEquals(
|
||||
[
|
||||
'type' => 'object',
|
||||
'properties' => [
|
||||
'money' => [
|
||||
'type' => 'number',
|
||||
'format' => 'float',
|
||||
],
|
||||
'id' => [
|
||||
'type' => 'integer',
|
||||
'description' => "User id",
|
||||
'readOnly' => true,
|
||||
'title' => "userid",
|
||||
'example' => 1,
|
||||
],
|
||||
'email' => [
|
||||
'type' => 'string',
|
||||
'readOnly' => false,
|
||||
],
|
||||
'friendsNumber' => [
|
||||
'type' => 'string',
|
||||
],
|
||||
'createdAt' => [
|
||||
'type' => 'string',
|
||||
'format' => 'date-time',
|
||||
],
|
||||
'users' => [
|
||||
'items' => [
|
||||
'$ref' => '#/definitions/User',
|
||||
],
|
||||
'type' => 'array',
|
||||
],
|
||||
'dummy' => [
|
||||
'$ref' => '#/definitions/Dummy2',
|
||||
],
|
||||
],
|
||||
],
|
||||
$this->getModel('User')->toArray()
|
||||
);
|
||||
}
|
||||
|
||||
public function testFormSupport()
|
||||
|
@ -18,12 +18,24 @@ class JMSFunctionalTest extends WebTestCase
|
||||
$this->assertEquals([
|
||||
'type' => 'object',
|
||||
'properties' => [
|
||||
'id' => ['type' => 'integer'],
|
||||
'email' => ['type' => 'string'],
|
||||
'id' => [
|
||||
'type' => 'integer',
|
||||
'description' => "User id",
|
||||
'readOnly' => true,
|
||||
'title' => "userid",
|
||||
'example' => 1,
|
||||
],
|
||||
'email' => [
|
||||
'type' => 'string',
|
||||
'readOnly' => false,
|
||||
],
|
||||
'roles' => [
|
||||
'type' => 'array',
|
||||
'items' => ['type' => 'string'],
|
||||
],
|
||||
'friendsNumber' => [
|
||||
'type' => 'string',
|
||||
],
|
||||
'friends' => [
|
||||
'type' => 'array',
|
||||
'items' => [
|
||||
|
Loading…
x
Reference in New Issue
Block a user