mirror of
https://github.com/retailcrm/NelmioApiDocBundle.git
synced 2025-02-02 15:51:48 +03:00
Support `@Model
` in in-object annotations
This commit is contained in:
parent
61cda0161c
commit
8026ff46eb
@ -13,6 +13,7 @@ namespace Nelmio\ApiDocBundle\ModelDescriber\Annotations;
|
||||
|
||||
use Doctrine\Common\Annotations\Reader;
|
||||
use EXSyst\Component\Swagger\Schema;
|
||||
use Nelmio\ApiDocBundle\Model\ModelRegistry;
|
||||
|
||||
/**
|
||||
* @internal
|
||||
@ -20,17 +21,19 @@ use EXSyst\Component\Swagger\Schema;
|
||||
class AnnotationsReader
|
||||
{
|
||||
private $annotationsReader;
|
||||
private $modelRegistry;
|
||||
|
||||
private $phpDocReader;
|
||||
private $swgAnnotationsReader;
|
||||
private $symfonyConstraintAnnotationReader;
|
||||
|
||||
public function __construct(Reader $annotationsReader)
|
||||
public function __construct(Reader $annotationsReader, ModelRegistry $modelRegistry)
|
||||
{
|
||||
$this->annotationsReader = $annotationsReader;
|
||||
$this->modelRegistry = $modelRegistry;
|
||||
|
||||
$this->phpDocReader = new PropertyPhpDocReader();
|
||||
$this->swgAnnotationsReader = new SwgAnnotationsReader($annotationsReader);
|
||||
$this->swgAnnotationsReader = new SwgAnnotationsReader($annotationsReader, $modelRegistry);
|
||||
$this->symfonyConstraintAnnotationReader = new SymfonyConstraintAnnotationReader($annotationsReader);
|
||||
}
|
||||
|
||||
@ -45,10 +48,10 @@ class AnnotationsReader
|
||||
return $this->swgAnnotationsReader->getPropertyName($reflectionProperty, $default);
|
||||
}
|
||||
|
||||
public function updateProperty(\ReflectionProperty $reflectionProperty, Schema $property)
|
||||
public function updateProperty(\ReflectionProperty $reflectionProperty, Schema $property, array $serializationGroups = null)
|
||||
{
|
||||
$this->phpDocReader->updateProperty($reflectionProperty, $property);
|
||||
$this->swgAnnotationsReader->updateProperty($reflectionProperty, $property);
|
||||
$this->swgAnnotationsReader->updateProperty($reflectionProperty, $property, $serializationGroups);
|
||||
$this->symfonyConstraintAnnotationReader->updateProperty($reflectionProperty, $property);
|
||||
}
|
||||
}
|
||||
|
@ -12,6 +12,7 @@
|
||||
namespace Nelmio\ApiDocBundle\ModelDescriber\Annotations;
|
||||
|
||||
use EXSyst\Component\Swagger\Schema;
|
||||
use Nelmio\ApiDocBundle\Model\Model;
|
||||
use phpDocumentor\Reflection\DocBlock\Tags\Var_;
|
||||
use phpDocumentor\Reflection\DocBlockFactory;
|
||||
|
||||
|
@ -13,8 +13,13 @@ namespace Nelmio\ApiDocBundle\ModelDescriber\Annotations;
|
||||
|
||||
use Doctrine\Common\Annotations\Reader;
|
||||
use EXSyst\Component\Swagger\Schema;
|
||||
use Nelmio\ApiDocBundle\Model\Model;
|
||||
use Nelmio\ApiDocBundle\Model\ModelRegistry;
|
||||
use Nelmio\ApiDocBundle\SwaggerPhp\ModelRegister;
|
||||
use Swagger\Analysis;
|
||||
use Swagger\Annotations\Definition as SwgDefinition;
|
||||
use Swagger\Annotations\Property as SwgProperty;
|
||||
use Swagger\Context;
|
||||
|
||||
/**
|
||||
* @internal
|
||||
@ -22,10 +27,12 @@ use Swagger\Annotations\Property as SwgProperty;
|
||||
class SwgAnnotationsReader
|
||||
{
|
||||
private $annotationsReader;
|
||||
private $modelRegister;
|
||||
|
||||
public function __construct(Reader $annotationsReader)
|
||||
public function __construct(Reader $annotationsReader, ModelRegistry $modelRegistry)
|
||||
{
|
||||
$this->annotationsReader = $annotationsReader;
|
||||
$this->modelRegister = new ModelRegister($modelRegistry);
|
||||
}
|
||||
|
||||
public function updateDefinition(\ReflectionClass $reflectionClass, Schema $schema)
|
||||
@ -35,9 +42,14 @@ class SwgAnnotationsReader
|
||||
return;
|
||||
}
|
||||
|
||||
if (null !== $swgDefinition->required) {
|
||||
$schema->setRequired($swgDefinition->required);
|
||||
// Read @Model annotations
|
||||
$this->modelRegister->__invoke(new Analysis([$swgDefinition]));
|
||||
|
||||
if (!$swgDefinition->validate()) {
|
||||
return;
|
||||
}
|
||||
|
||||
$schema->merge(json_decode(json_encode($swgDefinition)));
|
||||
}
|
||||
|
||||
public function getPropertyName(\ReflectionProperty $reflectionProperty, string $default): string
|
||||
@ -50,13 +62,24 @@ class SwgAnnotationsReader
|
||||
return $swgProperty->property ?? $default;
|
||||
}
|
||||
|
||||
public function updateProperty(\ReflectionProperty $reflectionProperty, Schema $property)
|
||||
public function updateProperty(\ReflectionProperty $reflectionProperty, Schema $property, array $serializationGroups = null)
|
||||
{
|
||||
/** @var SwgProperty $swgProperty */
|
||||
if (!$swgProperty = $this->annotationsReader->getPropertyAnnotation($reflectionProperty, SwgProperty::class)) {
|
||||
return;
|
||||
}
|
||||
|
||||
$declaringClass = $reflectionProperty->getDeclaringClass();
|
||||
$context = new Context([
|
||||
'namespace' => $declaringClass->getNamespaceName(),
|
||||
'class' => $declaringClass->getShortName(),
|
||||
'property' => $reflectionProperty->name,
|
||||
'filename' => $declaringClass->getFileName(),
|
||||
]);
|
||||
$swgProperty->_context = $context;
|
||||
|
||||
// Read @Model annotations
|
||||
$this->modelRegister->__invoke(new Analysis([$swgProperty]), $serializationGroups);
|
||||
|
||||
if (!$swgProperty->validate()) {
|
||||
return;
|
||||
}
|
||||
|
@ -13,7 +13,6 @@ namespace Nelmio\ApiDocBundle\ModelDescriber\Annotations;
|
||||
|
||||
use Doctrine\Common\Annotations\Reader;
|
||||
use EXSyst\Component\Swagger\Schema;
|
||||
use ReflectionProperty;
|
||||
use Symfony\Component\Validator\Constraints as Assert;
|
||||
|
||||
/**
|
||||
@ -39,7 +38,7 @@ class SymfonyConstraintAnnotationReader
|
||||
/**
|
||||
* Update the given property and schema with defined Symfony constraints.
|
||||
*/
|
||||
public function updateProperty(ReflectionProperty $reflectionProperty, Schema $property)
|
||||
public function updateProperty(\ReflectionProperty $reflectionProperty, Schema $property)
|
||||
{
|
||||
$annotations = $this->annotationsReader->getPropertyAnnotations($reflectionProperty);
|
||||
|
||||
@ -88,7 +87,7 @@ class SymfonyConstraintAnnotationReader
|
||||
/**
|
||||
* Set the required properties on the scheme.
|
||||
*/
|
||||
private function updateSchemaDefinitionWithRequiredProperty(ReflectionProperty $reflectionProperty)
|
||||
private function updateSchemaDefinitionWithRequiredProperty(\ReflectionProperty $reflectionProperty)
|
||||
{
|
||||
if (null === $this->schema) {
|
||||
return;
|
||||
|
@ -33,7 +33,7 @@ class JMSModelDescriber implements ModelDescriberInterface, ModelRegistryAwareIn
|
||||
|
||||
private $factory;
|
||||
private $namingStrategy;
|
||||
private $annotationsReader;
|
||||
private $doctrineReader;
|
||||
|
||||
public function __construct(
|
||||
MetadataFactoryInterface $factory,
|
||||
@ -42,7 +42,7 @@ class JMSModelDescriber implements ModelDescriberInterface, ModelRegistryAwareIn
|
||||
) {
|
||||
$this->factory = $factory;
|
||||
$this->namingStrategy = $namingStrategy;
|
||||
$this->annotationsReader = new AnnotationsReader($reader);
|
||||
$this->doctrineReader = $reader;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -59,7 +59,8 @@ class JMSModelDescriber implements ModelDescriberInterface, ModelRegistryAwareIn
|
||||
$groupsExclusion = null !== $model->getGroups() ? new GroupsExclusionStrategy($model->getGroups()) : null;
|
||||
|
||||
$schema->setType('object');
|
||||
$this->annotationsReader->updateDefinition(new \ReflectionClass($className), $schema);
|
||||
$annotationsReader = new AnnotationsReader($this->doctrineReader, $this->modelRegistry);
|
||||
$annotationsReader->updateDefinition(new \ReflectionClass($className), $schema);
|
||||
|
||||
$properties = $schema->getProperties();
|
||||
foreach ($metadata->propertyMetadata as $item) {
|
||||
@ -69,20 +70,26 @@ class JMSModelDescriber implements ModelDescriberInterface, ModelRegistryAwareIn
|
||||
}
|
||||
|
||||
$name = $this->namingStrategy->translateName($item);
|
||||
$groups = $model->getGroups();
|
||||
if (isset($groups[$name]) && is_array($groups[$name])) {
|
||||
$groups = $model->getGroups()[$name];
|
||||
}
|
||||
|
||||
// read property options from Swagger Property annotation if it exists
|
||||
if (null !== $item->reflection) {
|
||||
$property = $properties->get($this->annotationsReader->getPropertyName($item->reflection, $name));
|
||||
$this->annotationsReader->updateProperty($item->reflection, $property);
|
||||
$property = $properties->get($annotationsReader->getPropertyName($item->reflection, $name));
|
||||
$annotationsReader->updateProperty($item->reflection, $property, $groups);
|
||||
} else {
|
||||
$property = $properties->get($name);
|
||||
}
|
||||
|
||||
if (null !== $property->getType()) {
|
||||
if (null !== $property->getType() || null !== $property->getRef()) {
|
||||
continue;
|
||||
}
|
||||
if (null === $item->type) {
|
||||
$properties->remove($name);
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
if ($type = $this->getNestedTypeInArray($item)) {
|
||||
@ -108,12 +115,6 @@ class JMSModelDescriber implements ModelDescriberInterface, ModelRegistryAwareIn
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!isset($model->getGroups()[$name]) || !is_array($model->getGroups()[$name])) {
|
||||
$groups = $model->getGroups();
|
||||
} else {
|
||||
$groups = $model->getGroups()[$name];
|
||||
}
|
||||
|
||||
$property->setRef(
|
||||
$this->modelRegistry->register(new Model(new Type(Type::BUILTIN_TYPE_OBJECT, false, $type), $groups))
|
||||
);
|
||||
|
@ -25,7 +25,7 @@ class ObjectModelDescriber implements ModelDescriberInterface, ModelRegistryAwar
|
||||
use ModelRegistryAwareTrait;
|
||||
|
||||
private $propertyInfo;
|
||||
private $annotationsReader;
|
||||
private $doctrineReader;
|
||||
|
||||
private $swaggerDefinitionAnnotationReader;
|
||||
|
||||
@ -34,7 +34,7 @@ class ObjectModelDescriber implements ModelDescriberInterface, ModelRegistryAwar
|
||||
Reader $reader
|
||||
) {
|
||||
$this->propertyInfo = $propertyInfo;
|
||||
$this->annotationsReader = new AnnotationsReader($reader);
|
||||
$this->doctrineReader = $reader;
|
||||
}
|
||||
|
||||
public function describe(Model $model, Schema $schema)
|
||||
@ -47,7 +47,9 @@ class ObjectModelDescriber implements ModelDescriberInterface, ModelRegistryAwar
|
||||
if (null !== $model->getGroups()) {
|
||||
$context = ['serializer_groups' => $model->getGroups()];
|
||||
}
|
||||
$this->annotationsReader->updateDefinition(new \ReflectionClass($class), $schema);
|
||||
|
||||
$annotationsReader = new AnnotationsReader($this->doctrineReader, $this->modelRegistry);
|
||||
$annotationsReader->updateDefinition(new \ReflectionClass($class), $schema);
|
||||
|
||||
$propertyInfoProperties = $this->propertyInfo->getProperties($class, $context);
|
||||
if (null === $propertyInfoProperties) {
|
||||
@ -58,14 +60,20 @@ class ObjectModelDescriber implements ModelDescriberInterface, ModelRegistryAwar
|
||||
// read property options from Swagger Property annotation if it exists
|
||||
if (property_exists($class, $propertyName)) {
|
||||
$reflectionProperty = new \ReflectionProperty($class, $propertyName);
|
||||
$property = $properties->get($this->annotationsReader->getPropertyName($reflectionProperty, $propertyName));
|
||||
$this->annotationsReader->updateProperty($reflectionProperty, $property);
|
||||
$property = $properties->get($annotationsReader->getPropertyName($reflectionProperty, $propertyName));
|
||||
|
||||
$groups = $model->getGroups();
|
||||
if (isset($groups[$property]) && is_array($groups[$property])) {
|
||||
$groups = $model->getGroups()[$property];
|
||||
}
|
||||
|
||||
$annotationsReader->updateProperty($reflectionProperty, $property, $groups);
|
||||
} else {
|
||||
$property = $properties->get($propertyName);
|
||||
}
|
||||
|
||||
// If type manually defined
|
||||
if (null !== $property->getType()) {
|
||||
if (null !== $property->getType() || null !== $property->getRef()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
|
@ -262,6 +262,7 @@ General PHP objects
|
||||
|
||||
If you want to customize the documentation of a property of an object, you can use ``@SWG\Property``::
|
||||
|
||||
use NelmioApiDocBundle\Annotation\Model;
|
||||
use Swagger\Annotations as SWG;
|
||||
|
||||
class User
|
||||
@ -276,6 +277,11 @@ If you want to customize the documentation of a property of an object, you can u
|
||||
* @SWG\Property(type="string", maxLength=255)
|
||||
*/
|
||||
public $username;
|
||||
|
||||
/**
|
||||
* @SWG\Property(ref=@Model(type=User::class))
|
||||
*/
|
||||
public $friend;
|
||||
}
|
||||
|
||||
See the `OpenAPI 2.0 specification`__ to see all the available fields of ``@SWG\Property``.
|
||||
|
@ -15,6 +15,7 @@ use Nelmio\ApiDocBundle\Annotation\Model as ModelAnnotation;
|
||||
use Nelmio\ApiDocBundle\Model\Model;
|
||||
use Nelmio\ApiDocBundle\Model\ModelRegistry;
|
||||
use Swagger\Analysis;
|
||||
use Swagger\Annotations\AbstractAnnotation;
|
||||
use Swagger\Annotations\Items;
|
||||
use Swagger\Annotations\Parameter;
|
||||
use Swagger\Annotations\Response;
|
||||
@ -35,7 +36,7 @@ final class ModelRegister
|
||||
$this->modelRegistry = $modelRegistry;
|
||||
}
|
||||
|
||||
public function __invoke(Analysis $analysis)
|
||||
public function __invoke(Analysis $analysis, array $parentGroups = null)
|
||||
{
|
||||
$modelsRegistered = [];
|
||||
foreach ($analysis->annotations as $annotation) {
|
||||
@ -43,16 +44,10 @@ final class ModelRegister
|
||||
if ($annotation instanceof Schema && $annotation->ref instanceof ModelAnnotation) {
|
||||
$model = $annotation->ref;
|
||||
|
||||
$annotation->ref = $this->modelRegistry->register(new Model($this->createType($model->type), $model->groups));
|
||||
$annotation->ref = $this->modelRegistry->register(new Model($this->createType($model->type), $this->getGroups($model, $parentGroups)));
|
||||
|
||||
foreach ($annotation->_unmerged as $key => $unmerged) {
|
||||
if ($unmerged === $model) {
|
||||
unset($annotation->_unmerged[$key]);
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
$analysis->annotations->detach($model);
|
||||
// It is no longer an unmerged annotation
|
||||
$this->detach($model, $annotation, $analysis);
|
||||
|
||||
continue;
|
||||
}
|
||||
@ -95,21 +90,35 @@ final class ModelRegister
|
||||
}
|
||||
|
||||
$annotation->merge([new $annotationClass([
|
||||
'ref' => $this->modelRegistry->register(new Model($this->createType($model->type), $model->groups)),
|
||||
'ref' => $this->modelRegistry->register(new Model($this->createType($model->type), $this->getGroups($model, $parentGroups))),
|
||||
])]);
|
||||
|
||||
// It is no longer an unmerged annotation
|
||||
foreach ($annotation->_unmerged as $key => $unmerged) {
|
||||
if ($unmerged === $model) {
|
||||
unset($annotation->_unmerged[$key]);
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
$analysis->annotations->detach($model);
|
||||
$this->detach($model, $annotation, $analysis);
|
||||
}
|
||||
}
|
||||
|
||||
private function getGroups(ModelAnnotation $model, array $parentGroups = null)
|
||||
{
|
||||
if (null === $model->groups) {
|
||||
return $parentGroups;
|
||||
}
|
||||
|
||||
return array_merge($parentGroups ?? [], $model->groups);
|
||||
}
|
||||
|
||||
private function detach(ModelAnnotation $model, AbstractAnnotation $annotation, Analysis $analysis)
|
||||
{
|
||||
foreach ($annotation->_unmerged as $key => $unmerged) {
|
||||
if ($unmerged === $model) {
|
||||
unset($annotation->_unmerged[$key]);
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
$analysis->annotations->detach($model);
|
||||
}
|
||||
|
||||
private function createType(string $type): Type
|
||||
{
|
||||
if ('[]' === substr($type, -2)) {
|
||||
|
@ -12,11 +12,15 @@
|
||||
namespace Nelmio\ApiDocBundle\Tests\Functional\Entity;
|
||||
|
||||
use JMS\Serializer\Annotation as Serializer;
|
||||
use Nelmio\ApiDocBundle\Annotation\Model;
|
||||
use Swagger\Annotations as SWG;
|
||||
|
||||
/**
|
||||
* @Serializer\ExclusionPolicy("all")
|
||||
* @SWG\Definition(required={"id", "user"})
|
||||
* @SWG\Definition(
|
||||
* required={"id", "user"},
|
||||
* @SWG\Property(property="virtual", ref=@Model(type=JMSUser::class))
|
||||
* )
|
||||
*/
|
||||
class JMSComplex
|
||||
{
|
||||
@ -28,7 +32,7 @@ class JMSComplex
|
||||
private $id;
|
||||
|
||||
/**
|
||||
* @Serializer\Type("Nelmio\ApiDocBundle\Tests\Functional\Entity\JMSUser")
|
||||
* @SWG\Property(ref=@Model(type=JMSUser::class))
|
||||
* @Serializer\Expose
|
||||
* @Serializer\Groups({"details"})
|
||||
*/
|
||||
|
@ -78,6 +78,7 @@ class JMSFunctionalTest extends WebTestCase
|
||||
'id' => ['type' => 'integer'],
|
||||
'user' => ['$ref' => '#/definitions/JMSUser2'],
|
||||
'name' => ['type' => 'string'],
|
||||
'virtual' => ['$ref' => '#/definitions/JMSUser'],
|
||||
],
|
||||
'required' => [
|
||||
'id',
|
||||
|
Loading…
x
Reference in New Issue
Block a user