mirror of
https://github.com/retailcrm/NelmioApiDocBundle.git
synced 2025-02-02 15:51:48 +03:00
Add models support
This commit is contained in:
parent
936b8390f2
commit
14ae741115
29
Annotation/Model.php
Normal file
29
Annotation/Model.php
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
<?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\Annotation;
|
||||||
|
|
||||||
|
use Swagger\Annotations\AbstractAnnotation;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @Annotation
|
||||||
|
*/
|
||||||
|
final class Model extends AbstractAnnotation
|
||||||
|
{
|
||||||
|
public static $_required = ['type'];
|
||||||
|
public static $_parents = [
|
||||||
|
'Swagger\Annotations\Parameter',
|
||||||
|
'Swagger\Annotations\Response',
|
||||||
|
'Swagger\Annotations\Swagger',
|
||||||
|
];
|
||||||
|
|
||||||
|
public $type;
|
||||||
|
}
|
@ -13,20 +13,26 @@ namespace Nelmio\ApiDocBundle;
|
|||||||
|
|
||||||
use EXSyst\Component\Swagger\Swagger;
|
use EXSyst\Component\Swagger\Swagger;
|
||||||
use Nelmio\ApiDocBundle\Describer\DescriberInterface;
|
use Nelmio\ApiDocBundle\Describer\DescriberInterface;
|
||||||
|
use Nelmio\ApiDocBundle\Describer\ModelRegistryAwareInterface;
|
||||||
|
use Nelmio\ApiDocBundle\Model\ModelRegistry;
|
||||||
|
use Nelmio\ApiDocBundle\ModelDescriber\ModelDescriberInterface;
|
||||||
use Psr\Cache\CacheItemPoolInterface;
|
use Psr\Cache\CacheItemPoolInterface;
|
||||||
|
|
||||||
final class ApiDocGenerator
|
final class ApiDocGenerator
|
||||||
{
|
{
|
||||||
private $swagger;
|
private $swagger;
|
||||||
private $describers;
|
private $describers;
|
||||||
|
private $modelDescribers;
|
||||||
private $cacheItemPool;
|
private $cacheItemPool;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param DescriberInterface[] $describers
|
* @param DescriberInterface[] $describers
|
||||||
|
* @param ModelDescriberInterface[] $modelDescribers
|
||||||
*/
|
*/
|
||||||
public function __construct(array $describers, CacheItemPoolInterface $cacheItemPool = null)
|
public function __construct(array $describers, array $modelDescribers, CacheItemPoolInterface $cacheItemPool = null)
|
||||||
{
|
{
|
||||||
$this->describers = $describers;
|
$this->describers = $describers;
|
||||||
|
$this->modelDescribers = $modelDescribers;
|
||||||
$this->cacheItemPool = $cacheItemPool;
|
$this->cacheItemPool = $cacheItemPool;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -44,9 +50,15 @@ final class ApiDocGenerator
|
|||||||
}
|
}
|
||||||
|
|
||||||
$this->swagger = new Swagger();
|
$this->swagger = new Swagger();
|
||||||
|
$modelRegistry = new ModelRegistry($this->modelDescribers, $this->swagger);
|
||||||
foreach ($this->describers as $describer) {
|
foreach ($this->describers as $describer) {
|
||||||
|
if ($describer instanceof ModelRegistryAwareInterface) {
|
||||||
|
$describer->setModelRegistry($modelRegistry);
|
||||||
|
}
|
||||||
|
|
||||||
$describer->describe($this->swagger);
|
$describer->describe($this->swagger);
|
||||||
}
|
}
|
||||||
|
$modelRegistry->registerDefinitions();
|
||||||
|
|
||||||
if (isset($item)) {
|
if (isset($item)) {
|
||||||
$this->cacheItemPool->save($item->set($this->swagger));
|
$this->cacheItemPool->save($item->set($this->swagger));
|
||||||
|
30
DependencyInjection/Compiler/AddModelDescribersPass.php
Normal file
30
DependencyInjection/Compiler/AddModelDescribersPass.php
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
<?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\DependencyInjection\Compiler;
|
||||||
|
|
||||||
|
use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
|
||||||
|
use Symfony\Component\DependencyInjection\ContainerBuilder;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @internal
|
||||||
|
*/
|
||||||
|
final class AddModelDescribersPass implements CompilerPassInterface
|
||||||
|
{
|
||||||
|
use PriorityTaggedServiceTrait;
|
||||||
|
|
||||||
|
public function process(ContainerBuilder $container)
|
||||||
|
{
|
||||||
|
$modelDescribers = $this->findAndSortTaggedServices('nelmio_api_doc.model_describer', $container);
|
||||||
|
|
||||||
|
$container->getDefinition('nelmio_api_doc.generator')->replaceArgument(1, $modelDescribers);
|
||||||
|
}
|
||||||
|
}
|
@ -38,7 +38,7 @@ final class DefaultDescriber implements DescriberInterface
|
|||||||
$operation = $path->getOperation($method);
|
$operation = $path->getOperation($method);
|
||||||
|
|
||||||
// Default Response
|
// Default Response
|
||||||
if (0 === iterator_count($operation->getResponses())) {
|
if (0 === count($operation->getResponses())) {
|
||||||
$defaultResponse = $operation->getResponses()->get('default');
|
$defaultResponse = $operation->getResponses()->get('default');
|
||||||
$defaultResponse->setDescription('');
|
$defaultResponse->setDescription('');
|
||||||
}
|
}
|
||||||
|
19
Describer/ModelRegistryAwareInterface.php
Normal file
19
Describer/ModelRegistryAwareInterface.php
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
<?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\Describer;
|
||||||
|
|
||||||
|
use Nelmio\ApiDocBundle\Model\ModelRegistry;
|
||||||
|
|
||||||
|
interface ModelRegistryAwareInterface
|
||||||
|
{
|
||||||
|
public function setModelRegistry(ModelRegistry $modelRegistry);
|
||||||
|
}
|
24
Describer/ModelRegistryAwareTrait.php
Normal file
24
Describer/ModelRegistryAwareTrait.php
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
<?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\Describer;
|
||||||
|
|
||||||
|
use Nelmio\ApiDocBundle\Model\ModelRegistry;
|
||||||
|
|
||||||
|
trait ModelRegistryAwareTrait
|
||||||
|
{
|
||||||
|
private $modelRegistry;
|
||||||
|
|
||||||
|
public function setModelRegistry(ModelRegistry $modelRegistry)
|
||||||
|
{
|
||||||
|
$this->modelRegistry = $modelRegistry;
|
||||||
|
}
|
||||||
|
}
|
@ -17,8 +17,10 @@ use Nelmio\ApiDocBundle\Util\ControllerReflector;
|
|||||||
use Symfony\Component\Routing\Route;
|
use Symfony\Component\Routing\Route;
|
||||||
use Symfony\Component\Routing\RouteCollection;
|
use Symfony\Component\Routing\RouteCollection;
|
||||||
|
|
||||||
final class RouteDescriber implements DescriberInterface
|
final class RouteDescriber implements DescriberInterface, ModelRegistryAwareInterface
|
||||||
{
|
{
|
||||||
|
use ModelRegistryAwareTrait;
|
||||||
|
|
||||||
private $routeCollection;
|
private $routeCollection;
|
||||||
private $controllerReflector;
|
private $controllerReflector;
|
||||||
private $routeDescribers;
|
private $routeDescribers;
|
||||||
@ -51,6 +53,10 @@ final class RouteDescriber implements DescriberInterface
|
|||||||
if ($method = $this->controllerReflector->getReflectionMethod($controller)) {
|
if ($method = $this->controllerReflector->getReflectionMethod($controller)) {
|
||||||
// Extract as many informations as possible about this route
|
// Extract as many informations as possible about this route
|
||||||
foreach ($this->routeDescribers as $describer) {
|
foreach ($this->routeDescribers as $describer) {
|
||||||
|
if ($describer instanceof ModelRegistryAwareInterface) {
|
||||||
|
$describer->setModelRegistry($this->modelRegistry);
|
||||||
|
}
|
||||||
|
|
||||||
$describer->describe($api, $route, $method);
|
$describer->describe($api, $route, $method);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -11,37 +11,30 @@
|
|||||||
|
|
||||||
namespace Nelmio\ApiDocBundle\Describer;
|
namespace Nelmio\ApiDocBundle\Describer;
|
||||||
|
|
||||||
|
use Nelmio\ApiDocBundle\SwaggerPhp\AddDefaults;
|
||||||
|
use Nelmio\ApiDocBundle\SwaggerPhp\ModelRegister;
|
||||||
use Nelmio\ApiDocBundle\SwaggerPhp\OperationResolver;
|
use Nelmio\ApiDocBundle\SwaggerPhp\OperationResolver;
|
||||||
|
use Swagger\Analyser;
|
||||||
use Swagger\Analysis;
|
use Swagger\Analysis;
|
||||||
|
|
||||||
final class SwaggerPhpDescriber extends ExternalDocDescriber
|
final class SwaggerPhpDescriber extends ExternalDocDescriber implements ModelRegistryAwareInterface
|
||||||
{
|
{
|
||||||
|
use ModelRegistryAwareTrait;
|
||||||
|
|
||||||
private $operationResolver;
|
private $operationResolver;
|
||||||
|
|
||||||
public function __construct(string $projectPath, bool $overwrite = false)
|
public function __construct(string $projectPath, bool $overwrite = false)
|
||||||
{
|
{
|
||||||
|
$nelmioNamespace = 'Nelmio\\ApiDocBundle\\';
|
||||||
|
if (!in_array($nelmioNamespace, Analyser::$whitelist)) {
|
||||||
|
Analyser::$whitelist[] = $nelmioNamespace;
|
||||||
|
}
|
||||||
|
|
||||||
parent::__construct(function () use ($projectPath) {
|
parent::__construct(function () use ($projectPath) {
|
||||||
// Ignore notices as the documentation can be completed by other describers
|
$options = ['processors' => $this->getProcessors()];
|
||||||
$prevHandler = set_error_handler(function ($type, $message, $file, $line, $context) use (&$prevHandler) {
|
$annotation = \Swagger\scan($projectPath, $options);
|
||||||
if (E_USER_NOTICE === $type || E_USER_WARNING === $type) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
return null !== $prevHandler && call_user_func($prevHandler, $type, $message, $file, $line, $context);
|
return json_decode(json_encode($annotation));
|
||||||
});
|
|
||||||
|
|
||||||
try {
|
|
||||||
$options = [];
|
|
||||||
if (null !== $this->operationResolver) {
|
|
||||||
$options['processors'] = array_merge([$this->operationResolver], Analysis::processors());
|
|
||||||
}
|
|
||||||
|
|
||||||
$annotation = \Swagger\scan($projectPath, $options);
|
|
||||||
|
|
||||||
return json_decode(json_encode($annotation));
|
|
||||||
} finally {
|
|
||||||
restore_error_handler();
|
|
||||||
}
|
|
||||||
}, $overwrite);
|
}, $overwrite);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -53,4 +46,17 @@ final class SwaggerPhpDescriber extends ExternalDocDescriber
|
|||||||
{
|
{
|
||||||
$this->operationResolver = $operationResolver;
|
$this->operationResolver = $operationResolver;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private function getProcessors(): array
|
||||||
|
{
|
||||||
|
$processors = [
|
||||||
|
new AddDefaults(),
|
||||||
|
new ModelRegister($this->modelRegistry),
|
||||||
|
];
|
||||||
|
if (null !== $this->operationResolver) {
|
||||||
|
$processors[] = $this->operationResolver;
|
||||||
|
}
|
||||||
|
|
||||||
|
return array_merge($processors, Analysis::processors());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
45
EXSystApiDocBundle.php
Normal file
45
EXSystApiDocBundle.php
Normal file
@ -0,0 +1,45 @@
|
|||||||
|
<?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;
|
||||||
|
|
||||||
|
use Nelmio\ApiDocBundle\DependencyInjection\Compiler\AddDescribersPass;
|
||||||
|
use Nelmio\ApiDocBundle\DependencyInjection\Compiler\AddModelDescribersPass;
|
||||||
|
use Nelmio\ApiDocBundle\DependencyInjection\Compiler\AddRouteDescribersPass;
|
||||||
|
use Nelmio\ApiDocBundle\DependencyInjection\EXSystApiDocExtension;
|
||||||
|
use Symfony\Component\DependencyInjection\ContainerBuilder;
|
||||||
|
use Symfony\Component\HttpKernel\Bundle\Bundle;
|
||||||
|
|
||||||
|
final class EXSystApiDocBundle extends Bundle
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
public function build(ContainerBuilder $container)
|
||||||
|
{
|
||||||
|
$container->addCompilerPass(new AddDescribersPass());
|
||||||
|
$container->addCompilerPass(new AddRouteDescribersPass());
|
||||||
|
$container->addCompilerPass(new AddModelDescribersPass());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
public function getContainerExtension()
|
||||||
|
{
|
||||||
|
if (null === $this->extension) {
|
||||||
|
$this->extension = new EXSystApiDocExtension();
|
||||||
|
}
|
||||||
|
if ($this->extension) {
|
||||||
|
return $this->extension;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
37
Model/Model.php
Normal file
37
Model/Model.php
Normal file
@ -0,0 +1,37 @@
|
|||||||
|
<?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\Model;
|
||||||
|
|
||||||
|
use Symfony\Component\PropertyInfo\Type;
|
||||||
|
|
||||||
|
final class Model
|
||||||
|
{
|
||||||
|
private $type;
|
||||||
|
|
||||||
|
public function __construct(Type $type)
|
||||||
|
{
|
||||||
|
$this->type = $type;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return Type|null
|
||||||
|
*/
|
||||||
|
public function getType()
|
||||||
|
{
|
||||||
|
return $this->type;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getHash()
|
||||||
|
{
|
||||||
|
return md5(serialize($this->type));
|
||||||
|
}
|
||||||
|
}
|
132
Model/ModelRegistry.php
Normal file
132
Model/ModelRegistry.php
Normal file
@ -0,0 +1,132 @@
|
|||||||
|
<?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\Model;
|
||||||
|
|
||||||
|
use EXSyst\Component\Swagger\Schema;
|
||||||
|
use EXSyst\Component\Swagger\Swagger;
|
||||||
|
use Nelmio\ApiDocBundle\Describer\ModelRegistryAwareInterface;
|
||||||
|
use Nelmio\ApiDocBundle\ModelDescriber\ModelDescriberInterface;
|
||||||
|
use Symfony\Component\PropertyInfo\Type;
|
||||||
|
|
||||||
|
final class ModelRegistry
|
||||||
|
{
|
||||||
|
private $unregistered = [];
|
||||||
|
private $models = [];
|
||||||
|
private $names = [];
|
||||||
|
private $modelDescribers = [];
|
||||||
|
private $api;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param ModelDescriberInterface[] $modelDescribers
|
||||||
|
*
|
||||||
|
* @internal
|
||||||
|
*/
|
||||||
|
public function __construct(array $modelDescribers, Swagger $api)
|
||||||
|
{
|
||||||
|
$this->modelDescribers = $modelDescribers;
|
||||||
|
$this->api = $api;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function register(Model $model): string
|
||||||
|
{
|
||||||
|
$hash = $model->getHash();
|
||||||
|
if (isset($this->names[$hash])) {
|
||||||
|
return '#/definitions/'.$this->names[$hash];
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->names[$hash] = $name = $this->generateModelName($model);
|
||||||
|
$this->models[$hash] = $model;
|
||||||
|
$this->unregistered[] = $hash;
|
||||||
|
|
||||||
|
// Reserve the name
|
||||||
|
$this->api->getDefinitions()->get($name);
|
||||||
|
|
||||||
|
return '#/definitions/'.$name;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @internal
|
||||||
|
*/
|
||||||
|
public function registerDefinitions()
|
||||||
|
{
|
||||||
|
while (count($this->unregistered)) {
|
||||||
|
$tmp = [];
|
||||||
|
foreach ($this->unregistered as $hash) {
|
||||||
|
$tmp[$this->names[$hash]] = $this->models[$hash];
|
||||||
|
}
|
||||||
|
$this->unregistered = [];
|
||||||
|
|
||||||
|
foreach ($tmp as $name => $model) {
|
||||||
|
$schema = null;
|
||||||
|
foreach ($this->modelDescribers as $modelDescriber) {
|
||||||
|
if ($modelDescriber instanceof ModelRegistryAwareInterface) {
|
||||||
|
$modelDescriber->setModelRegistry($this);
|
||||||
|
}
|
||||||
|
if ($modelDescriber->supports($model)) {
|
||||||
|
$schema = new Schema();
|
||||||
|
$modelDescriber->describe($model, $schema);
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (null === $schema) {
|
||||||
|
throw new \LogicException(sprintf('Schema of type "%s" can\'t be generated, no describer supports it.', $this->typeToString($model->getType())));
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->api->getDefinitions()->set($name, $schema);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private function generateModelName(Model $model): string
|
||||||
|
{
|
||||||
|
$definitions = $this->api->getDefinitions();
|
||||||
|
$base = $name = $this->getTypeShortName($model->getType());
|
||||||
|
$i = 1;
|
||||||
|
while ($definitions->has($name)) {
|
||||||
|
++$i;
|
||||||
|
$name = $base.$i;
|
||||||
|
}
|
||||||
|
|
||||||
|
return $name;
|
||||||
|
}
|
||||||
|
|
||||||
|
private function getTypeShortName(Type $type)
|
||||||
|
{
|
||||||
|
if (null !== $type->getCollectionValueType()) {
|
||||||
|
return $this->getTypeShortName($type->getCollectionValueType()).'[]';
|
||||||
|
}
|
||||||
|
if (Type::BUILTIN_TYPE_OBJECT === $type->getBuiltinType()) {
|
||||||
|
$parts = explode('\\', $type->getClassName());
|
||||||
|
|
||||||
|
return end($parts);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $type->getBuiltinType();
|
||||||
|
}
|
||||||
|
|
||||||
|
private function typeToString(Type $type): string
|
||||||
|
{
|
||||||
|
if (Type::BUILTIN_TYPE_OBJECT === $type->getBuiltinType()) {
|
||||||
|
return $type->getClassName();
|
||||||
|
} elseif ($type->isCollection()) {
|
||||||
|
if (null !== $type->getCollectionValueType()) {
|
||||||
|
return $this->typeToString($type->getCollectionValueType()).'[]';
|
||||||
|
} else {
|
||||||
|
return 'mixed[]';
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return $type->getBuiltinType();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
35
ModelDescriber/CollectionModelDescriber.php
Normal file
35
ModelDescriber/CollectionModelDescriber.php
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
<?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()))
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function supports(Model $model)
|
||||||
|
{
|
||||||
|
return $model->getType()->isCollection() && null !== $model->getType()->getCollectionValueType();
|
||||||
|
}
|
||||||
|
}
|
22
ModelDescriber/ModelDescriberInterface.php
Normal file
22
ModelDescriber/ModelDescriberInterface.php
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
<?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;
|
||||||
|
|
||||||
|
interface ModelDescriberInterface
|
||||||
|
{
|
||||||
|
public function describe(Model $model, Schema $schema);
|
||||||
|
|
||||||
|
public function supports(Model $model);
|
||||||
|
}
|
55
ModelDescriber/ObjectModelDescriber.php
Normal file
55
ModelDescriber/ObjectModelDescriber.php
Normal file
@ -0,0 +1,55 @@
|
|||||||
|
<?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;
|
||||||
|
use Symfony\Component\PropertyInfo\PropertyInfoExtractorInterface;
|
||||||
|
use Symfony\Component\PropertyInfo\Type;
|
||||||
|
|
||||||
|
class ObjectModelDescriber implements ModelDescriberInterface, ModelRegistryAwareInterface
|
||||||
|
{
|
||||||
|
use ModelRegistryAwareTrait;
|
||||||
|
|
||||||
|
public function __construct(PropertyInfoExtractorInterface $propertyInfo)
|
||||||
|
{
|
||||||
|
$this->propertyInfo = $propertyInfo;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function describe(Model $model, Schema $schema)
|
||||||
|
{
|
||||||
|
$schema->setType('object');
|
||||||
|
$properties = $schema->getProperties();
|
||||||
|
|
||||||
|
$class = $model->getType()->getClassName();
|
||||||
|
foreach ($this->propertyInfo->getProperties($class) as $propertyName) {
|
||||||
|
$types = $this->propertyInfo->getTypes($class, $propertyName);
|
||||||
|
if (0 === count($types)) {
|
||||||
|
throw new \LogicException(sprintf('The PropertyInfo component was not able to guess the type of %s::$%s', $class, $propertyName));
|
||||||
|
}
|
||||||
|
if (count($types) > 1) {
|
||||||
|
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]))
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public function supports(Model $model)
|
||||||
|
{
|
||||||
|
return Type::BUILTIN_TYPE_OBJECT === $model->getType()->getBuiltinType();
|
||||||
|
}
|
||||||
|
}
|
36
ModelDescriber/ScalarModelDescriber.php
Normal file
36
ModelDescriber/ScalarModelDescriber.php
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
<?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',
|
||||||
|
];
|
||||||
|
|
||||||
|
public function describe(Model $model, Schema $schema)
|
||||||
|
{
|
||||||
|
$type = self::$supportedTypes[$model->getType()->getBuiltinType()];
|
||||||
|
$schema->setType($type);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function supports(Model $model)
|
||||||
|
{
|
||||||
|
return isset(self::$supportedTypes[$model->getType()->getBuiltinType()]);
|
||||||
|
}
|
||||||
|
}
|
@ -12,6 +12,7 @@
|
|||||||
namespace Nelmio\ApiDocBundle;
|
namespace Nelmio\ApiDocBundle;
|
||||||
|
|
||||||
use Nelmio\ApiDocBundle\DependencyInjection\Compiler\AddDescribersPass;
|
use Nelmio\ApiDocBundle\DependencyInjection\Compiler\AddDescribersPass;
|
||||||
|
use Nelmio\ApiDocBundle\DependencyInjection\Compiler\AddModelDescribersPass;
|
||||||
use Nelmio\ApiDocBundle\DependencyInjection\Compiler\AddRouteDescribersPass;
|
use Nelmio\ApiDocBundle\DependencyInjection\Compiler\AddRouteDescribersPass;
|
||||||
use Symfony\Component\DependencyInjection\ContainerBuilder;
|
use Symfony\Component\DependencyInjection\ContainerBuilder;
|
||||||
use Symfony\Component\HttpKernel\Bundle\Bundle;
|
use Symfony\Component\HttpKernel\Bundle\Bundle;
|
||||||
@ -24,6 +25,7 @@ final class NelmioApiDocBundle extends Bundle
|
|||||||
public function build(ContainerBuilder $container)
|
public function build(ContainerBuilder $container)
|
||||||
{
|
{
|
||||||
$container->addCompilerPass(new AddDescribersPass());
|
$container->addCompilerPass(new AddDescribersPass());
|
||||||
|
$container->addCompilerPass(new AddModelDescribersPass());
|
||||||
$container->addCompilerPass(new AddRouteDescribersPass());
|
$container->addCompilerPass(new AddRouteDescribersPass());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -8,7 +8,7 @@
|
|||||||
<argument type="service" id="nelmio_api_doc.describers.api_platform.documentation" />
|
<argument type="service" id="nelmio_api_doc.describers.api_platform.documentation" />
|
||||||
<argument type="service" id="api_platform.swagger.normalizer.documentation" />
|
<argument type="service" id="api_platform.swagger.normalizer.documentation" />
|
||||||
|
|
||||||
<tag name="nelmio_api_doc.describer" priority="-200" />
|
<tag name="nelmio_api_doc.describer" priority="-100" />
|
||||||
</service>
|
</service>
|
||||||
|
|
||||||
<service id="nelmio_api_doc.describers.api_platform.documentation" class="ApiPlatform\Core\Documentation\Documentation" public="false">
|
<service id="nelmio_api_doc.describers.api_platform.documentation" class="ApiPlatform\Core\Documentation\Documentation" public="false">
|
||||||
|
@ -5,7 +5,8 @@
|
|||||||
|
|
||||||
<services>
|
<services>
|
||||||
<service id="nelmio_api_doc.generator" class="Nelmio\ApiDocBundle\ApiDocGenerator">
|
<service id="nelmio_api_doc.generator" class="Nelmio\ApiDocBundle\ApiDocGenerator">
|
||||||
<argument type="collection" />
|
<argument type="collection" /> <!-- Describers -->
|
||||||
|
<argument type="collection" /> <!-- Model Describers -->
|
||||||
</service>
|
</service>
|
||||||
|
|
||||||
<service id="nelmio_api_doc.controller_reflector" class="Nelmio\ApiDocBundle\Util\ControllerReflector" public="false">
|
<service id="nelmio_api_doc.controller_reflector" class="Nelmio\ApiDocBundle\Util\ControllerReflector" public="false">
|
||||||
@ -18,7 +19,6 @@
|
|||||||
<argument type="collection" /> <!-- Path patterns -->
|
<argument type="collection" /> <!-- Path patterns -->
|
||||||
</service>
|
</service>
|
||||||
|
|
||||||
|
|
||||||
<service id="nelmio_api_doc.describers.route" class="Nelmio\ApiDocBundle\Describer\RouteDescriber" public="false">
|
<service id="nelmio_api_doc.describers.route" class="Nelmio\ApiDocBundle\Describer\RouteDescriber" public="false">
|
||||||
<argument type="service">
|
<argument type="service">
|
||||||
<service class="Symfony\Component\Routing\RouteCollection">
|
<service class="Symfony\Component\Routing\RouteCollection">
|
||||||
@ -33,16 +33,31 @@
|
|||||||
<argument type="service" id="nelmio_api_doc.controller_reflector" />
|
<argument type="service" id="nelmio_api_doc.controller_reflector" />
|
||||||
<argument type="collection" />
|
<argument type="collection" />
|
||||||
|
|
||||||
<tag name="nelmio_api_doc.describer" priority="-100" />
|
<tag name="nelmio_api_doc.describer" priority="-400" />
|
||||||
</service>
|
</service>
|
||||||
|
|
||||||
<service id="nelmio_api_doc.describers.default" class="Nelmio\ApiDocBundle\Describer\DefaultDescriber" public="false">
|
<service id="nelmio_api_doc.describers.default" class="Nelmio\ApiDocBundle\Describer\DefaultDescriber" public="false">
|
||||||
<tag name="nelmio_api_doc.describer" priority="-1000" />
|
<tag name="nelmio_api_doc.describer" priority="-1000" />
|
||||||
</service>
|
</service>
|
||||||
|
|
||||||
<!-- Routing Extractors -->
|
<!-- Routing Describers -->
|
||||||
<service id="nelmio_api_doc.route_describers.route_metadata" class="Nelmio\ApiDocBundle\RouteDescriber\RouteMetadataDescriber" public="false">
|
<service id="nelmio_api_doc.route_describers.route_metadata" class="Nelmio\ApiDocBundle\RouteDescriber\RouteMetadataDescriber" public="false">
|
||||||
<tag name="nelmio_api_doc.route_describer" priority="-100" />
|
<tag name="nelmio_api_doc.route_describer" priority="-300" />
|
||||||
|
</service>
|
||||||
|
|
||||||
|
<!-- Model Describers -->
|
||||||
|
<service id="nelmio_api_doc.model_describers.object" class="Nelmio\ApiDocBundle\ModelDescriber\ObjectModelDescriber" public="false">
|
||||||
|
<argument type="service" id="property_info" />
|
||||||
|
|
||||||
|
<tag name="nelmio_api_doc.model_describer" />
|
||||||
|
</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>
|
</service>
|
||||||
</services>
|
</services>
|
||||||
|
|
||||||
|
@ -10,7 +10,16 @@
|
|||||||
<argument type="service" id="nelmio_api_doc.describers.swagger_php.operation_resolver" />
|
<argument type="service" id="nelmio_api_doc.describers.swagger_php.operation_resolver" />
|
||||||
</call>
|
</call>
|
||||||
|
|
||||||
<tag name="nelmio_api_doc.describer" priority="-300" />
|
<tag name="nelmio_api_doc.describer" priority="-200" />
|
||||||
|
</service>
|
||||||
|
|
||||||
|
<service id="nelmio_api_doc.describers.swagger_php.path_resolver" class="Nelmio\ApiDocBundle\SwaggerPhp\PathResolver" public="false">
|
||||||
|
<argument type="service">
|
||||||
|
<service class="Symfony\Component\Routing\RouteCollection">
|
||||||
|
<factory service="router" method="getRouteCollection" />
|
||||||
|
</service>
|
||||||
|
</argument>
|
||||||
|
<argument type="service" id="nelmio_api_doc.controller_reflector" />
|
||||||
</service>
|
</service>
|
||||||
|
|
||||||
<service id="nelmio_api_doc.describers.swagger_php.operation_resolver" class="Nelmio\ApiDocBundle\SwaggerPhp\OperationResolver" public="false">
|
<service id="nelmio_api_doc.describers.swagger_php.operation_resolver" class="Nelmio\ApiDocBundle\SwaggerPhp\OperationResolver" public="false">
|
||||||
|
37
SwaggerPhp/AddDefaults.php
Normal file
37
SwaggerPhp/AddDefaults.php
Normal file
@ -0,0 +1,37 @@
|
|||||||
|
<?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\SwaggerPhp;
|
||||||
|
|
||||||
|
use Swagger\Analysis;
|
||||||
|
use Swagger\Annotations\Info;
|
||||||
|
use Swagger\Annotations\Swagger;
|
||||||
|
use Swagger\Context;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add defaults to fix default warnings.
|
||||||
|
*
|
||||||
|
* @internal
|
||||||
|
*/
|
||||||
|
final class AddDefaults
|
||||||
|
{
|
||||||
|
public function __invoke(Analysis $analysis)
|
||||||
|
{
|
||||||
|
if ($analysis->getAnnotationsOfType(Info::class)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (($annotations = $analysis->getAnnotationsOfType(Swagger::class)) && null !== $annotations[0]->info) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$analysis->addAnnotation(new Info(['title' => '', 'version' => '0.0.0', '_context' => new Context(['generated' => true])]), null);
|
||||||
|
}
|
||||||
|
}
|
84
SwaggerPhp/ModelRegister.php
Normal file
84
SwaggerPhp/ModelRegister.php
Normal file
@ -0,0 +1,84 @@
|
|||||||
|
<?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\SwaggerPhp;
|
||||||
|
|
||||||
|
use Nelmio\ApiDocBundle\Annotation\Model as ModelAnnotation;
|
||||||
|
use Nelmio\ApiDocBundle\Model\Model;
|
||||||
|
use Nelmio\ApiDocBundle\Model\ModelRegistry;
|
||||||
|
use Swagger\Analysis;
|
||||||
|
use Swagger\Annotations\Items;
|
||||||
|
use Swagger\Annotations\Parameter;
|
||||||
|
use Swagger\Annotations\Response;
|
||||||
|
use Swagger\Annotations\Schema;
|
||||||
|
use Symfony\Component\PropertyInfo\Type;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Resolves the path in SwaggerPhp annotation when needed.
|
||||||
|
*
|
||||||
|
* @internal
|
||||||
|
*/
|
||||||
|
final class ModelRegister
|
||||||
|
{
|
||||||
|
private $modelRegistry;
|
||||||
|
|
||||||
|
public function __construct(ModelRegistry $modelRegistry)
|
||||||
|
{
|
||||||
|
$this->modelRegistry = $modelRegistry;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function __invoke(Analysis $analysis)
|
||||||
|
{
|
||||||
|
foreach ($analysis->annotations as $annotation) {
|
||||||
|
if (!$annotation instanceof ModelAnnotation || $annotation->_context->not('nested')) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!is_string($annotation->type)) {
|
||||||
|
// Ignore invalid annotations, they are validated later
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
$parent = $annotation->_context->nested;
|
||||||
|
if (!$parent instanceof Response && !$parent instanceof Parameter && !$parent instanceof Schema) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
$annotationClass = Schema::class;
|
||||||
|
if ($parent instanceof Schema) {
|
||||||
|
$annotationClass = Items::class;
|
||||||
|
}
|
||||||
|
|
||||||
|
$parent->merge([new $annotationClass([
|
||||||
|
'ref' => $this->modelRegistry->register(new Model($this->createType($annotation->type))),
|
||||||
|
])]);
|
||||||
|
|
||||||
|
// It is no longer an unmerged annotation
|
||||||
|
foreach ($parent->_unmerged as $key => $unmerged) {
|
||||||
|
if ($unmerged === $annotation) {
|
||||||
|
unset($parent->_unmerged[$key]);
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
$analysis->annotations->detach($annotation);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private function createType(string $type): Type
|
||||||
|
{
|
||||||
|
if ('[]' === substr($type, -2)) {
|
||||||
|
return new Type(Type::BUILTIN_TYPE_ARRAY, false, null, true, null, $this->createType(substr($type, 0, -2)));
|
||||||
|
}
|
||||||
|
|
||||||
|
return new Type(Type::BUILTIN_TYPE_OBJECT, false, $type);
|
||||||
|
}
|
||||||
|
}
|
@ -11,7 +11,9 @@
|
|||||||
|
|
||||||
namespace Nelmio\ApiDocBundle\Tests\Describer;
|
namespace Nelmio\ApiDocBundle\Tests\Describer;
|
||||||
|
|
||||||
|
use EXSyst\Component\Swagger\Swagger;
|
||||||
use Nelmio\ApiDocBundle\Describer\SwaggerPhpDescriber;
|
use Nelmio\ApiDocBundle\Describer\SwaggerPhpDescriber;
|
||||||
|
use Nelmio\ApiDocBundle\Model\ModelRegistry;
|
||||||
|
|
||||||
class SwaggerPhpDescriberTest extends AbstractDescriberTest
|
class SwaggerPhpDescriberTest extends AbstractDescriberTest
|
||||||
{
|
{
|
||||||
@ -27,5 +29,6 @@ class SwaggerPhpDescriberTest extends AbstractDescriberTest
|
|||||||
protected function setUp()
|
protected function setUp()
|
||||||
{
|
{
|
||||||
$this->describer = new SwaggerPhpDescriber(__DIR__.'/../Fixtures');
|
$this->describer = new SwaggerPhpDescriber(__DIR__.'/../Fixtures');
|
||||||
|
$this->describer->setModelRegistry(new ModelRegistry([], new Swagger()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -14,6 +14,9 @@ namespace Nelmio\ApiDocBundle\Tests\Functional\Controller;
|
|||||||
use FOS\RestBundle\Controller\Annotations\QueryParam;
|
use FOS\RestBundle\Controller\Annotations\QueryParam;
|
||||||
use FOS\RestBundle\Controller\Annotations\RequestParam;
|
use FOS\RestBundle\Controller\Annotations\RequestParam;
|
||||||
use Nelmio\ApiDocBundle\Annotation\ApiDoc;
|
use Nelmio\ApiDocBundle\Annotation\ApiDoc;
|
||||||
|
use Nelmio\ApiDocBundle\Annotation\Model;
|
||||||
|
use Nelmio\ApiDocBundle\Tests\Functional\Entity\Dummy;
|
||||||
|
use Nelmio\ApiDocBundle\Tests\Functional\Entity\User;
|
||||||
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route;
|
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route;
|
||||||
use Swagger\Annotations as SWG;
|
use Swagger\Annotations as SWG;
|
||||||
|
|
||||||
@ -35,11 +38,19 @@ class ApiController
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* @Route("/swagger/implicit", methods={"GET", "POST"})
|
* @Route("/swagger/implicit", methods={"GET", "POST"})
|
||||||
* @SWG\Response(response="201", description="Operation automatically detected")
|
* @SWG\Response(
|
||||||
|
* response="201",
|
||||||
|
* description="Operation automatically detected",
|
||||||
|
* @Model(type="Nelmio\ApiDocBundle\Tests\Functional\Entity\User")
|
||||||
|
* )
|
||||||
* @SWG\Parameter(
|
* @SWG\Parameter(
|
||||||
* name="foo",
|
* name="foo",
|
||||||
* in="query",
|
* in="body",
|
||||||
* description="This is a parameter"
|
* description="This is a parameter",
|
||||||
|
* @SWG\Schema(
|
||||||
|
* type="array",
|
||||||
|
* @Model(type="Nelmio\ApiDocBundle\Tests\Functional\Entity\User")
|
||||||
|
* )
|
||||||
* )
|
* )
|
||||||
*/
|
*/
|
||||||
public function implicitSwaggerAction()
|
public function implicitSwaggerAction()
|
||||||
|
@ -42,11 +42,6 @@ class Dummy
|
|||||||
*/
|
*/
|
||||||
private $name;
|
private $name;
|
||||||
|
|
||||||
/**
|
|
||||||
* @var array
|
|
||||||
*/
|
|
||||||
private $foo;
|
|
||||||
|
|
||||||
public function getId(): int
|
public function getId(): int
|
||||||
{
|
{
|
||||||
return $this->id;
|
return $this->id;
|
||||||
@ -65,8 +60,4 @@ class Dummy
|
|||||||
public function hasRole(string $role)
|
public function hasRole(string $role)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
public function setFoo(array $foo = null)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
26
Tests/Functional/Entity/User.php
Normal file
26
Tests/Functional/Entity/User.php
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
<?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\Tests\Functional\Entity;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Guilhem N. <egetick@gmail.com>
|
||||||
|
*/
|
||||||
|
class User
|
||||||
|
{
|
||||||
|
public function addUsers(User $user)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setDummy(Dummy $dummy)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
@ -11,6 +11,8 @@
|
|||||||
|
|
||||||
namespace Nelmio\ApiDocBundle\Tests\Functional;
|
namespace Nelmio\ApiDocBundle\Tests\Functional;
|
||||||
|
|
||||||
|
use EXSyst\Component\Swagger\Operation;
|
||||||
|
use EXSyst\Component\Swagger\Schema;
|
||||||
use Symfony\Bundle\FrameworkBundle\Test\WebTestCase;
|
use Symfony\Bundle\FrameworkBundle\Test\WebTestCase;
|
||||||
|
|
||||||
class FunctionalTest extends WebTestCase
|
class FunctionalTest extends WebTestCase
|
||||||
@ -50,11 +52,16 @@ class FunctionalTest extends WebTestCase
|
|||||||
|
|
||||||
$responses = $operation->getResponses();
|
$responses = $operation->getResponses();
|
||||||
$this->assertTrue($responses->has('201'));
|
$this->assertTrue($responses->has('201'));
|
||||||
$this->assertEquals('Operation automatically detected', $responses->get('201')->getDescription());
|
$response = $responses->get('201');
|
||||||
|
$this->assertEquals('Operation automatically detected', $response->getDescription());
|
||||||
|
$this->assertEquals('#/definitions/User', $response->getSchema()->getRef());
|
||||||
|
|
||||||
$parameters = $operation->getParameters();
|
$parameters = $operation->getParameters();
|
||||||
$this->assertTrue($parameters->has('foo', 'query'));
|
$this->assertTrue($parameters->has('foo', 'body'));
|
||||||
$this->assertEquals('This is a parameter', $parameters->get('foo', 'query')->getDescription());
|
$parameter = $parameters->get('foo', 'body');
|
||||||
|
|
||||||
|
$this->assertEquals('This is a parameter', $parameter->getDescription());
|
||||||
|
$this->assertEquals('#/definitions/User', $parameter->getSchema()->getItems()->getRef());
|
||||||
}
|
}
|
||||||
|
|
||||||
public function implicitSwaggerActionMethodsProvider()
|
public function implicitSwaggerActionMethodsProvider()
|
||||||
@ -109,6 +116,27 @@ class FunctionalTest extends WebTestCase
|
|||||||
$operation = $this->getOperation('/api/dummies/{id}', 'get');
|
$operation = $this->getOperation('/api/dummies/{id}', 'get');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function testUserModel()
|
||||||
|
{
|
||||||
|
$model = $this->getModel('User');
|
||||||
|
$this->assertEquals('object', $model->getType());
|
||||||
|
$properties = $model->getProperties();
|
||||||
|
|
||||||
|
$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());
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testUsersModel()
|
||||||
|
{
|
||||||
|
$model = $this->getModel('User[]');
|
||||||
|
$this->assertEquals('array', $model->getType());
|
||||||
|
|
||||||
|
$this->assertEquals('#/definitions/User', $model->getItems()->getRef());
|
||||||
|
}
|
||||||
|
|
||||||
private function getSwaggerDefinition()
|
private function getSwaggerDefinition()
|
||||||
{
|
{
|
||||||
static::createClient();
|
static::createClient();
|
||||||
@ -116,12 +144,20 @@ class FunctionalTest extends WebTestCase
|
|||||||
return static::$kernel->getContainer()->get('nelmio_api_doc.generator')->generate();
|
return static::$kernel->getContainer()->get('nelmio_api_doc.generator')->generate();
|
||||||
}
|
}
|
||||||
|
|
||||||
private function getOperation($path, $method)
|
private function getModel($name): Schema
|
||||||
|
{
|
||||||
|
$definitions = $this->getSwaggerDefinition()->getDefinitions();
|
||||||
|
$this->assertTrue($definitions->has($name));
|
||||||
|
|
||||||
|
return $definitions->get($name);
|
||||||
|
}
|
||||||
|
|
||||||
|
private function getOperation($path, $method): Operation
|
||||||
{
|
{
|
||||||
$api = $this->getSwaggerDefinition();
|
$api = $this->getSwaggerDefinition();
|
||||||
$paths = $api->getPaths();
|
$paths = $api->getPaths();
|
||||||
|
|
||||||
$this->assertTrue($paths->has($path), sprintf('Path "%s" does not exist', $path));
|
$this->assertTrue($paths->has($path), sprintf('Path "%s" does not exist.', $path));
|
||||||
$action = $paths->get($path);
|
$action = $paths->get($path);
|
||||||
|
|
||||||
$this->assertTrue($action->hasOperation($method), sprintf('Operation "%s" for path "%s" does not exist', $path, $method));
|
$this->assertTrue($action->hasOperation($method), sprintf('Operation "%s" for path "%s" does not exist', $path, $method));
|
||||||
|
41
Tests/Model/ModelRegistryTest.php
Normal file
41
Tests/Model/ModelRegistryTest.php
Normal file
@ -0,0 +1,41 @@
|
|||||||
|
<?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\Tests\Model;
|
||||||
|
|
||||||
|
use EXSyst\Component\Swagger\Schema;
|
||||||
|
use EXSyst\Component\Swagger\Swagger;
|
||||||
|
use Nelmio\ApiDocBundle\Model\Model;
|
||||||
|
use Nelmio\ApiDocBundle\Model\ModelRegistry;
|
||||||
|
use Symfony\Component\PropertyInfo\Type;
|
||||||
|
|
||||||
|
class ModelRegistryTest extends \PHPUnit_Framework_TestCase
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @dataProvider unsupportedTypesProvider
|
||||||
|
*/
|
||||||
|
public function testUnsupportedTypeException(Type $type, string $stringType)
|
||||||
|
{
|
||||||
|
$this->setExpectedException('\LogicException', sprintf('Schema of type "%s" can\'t be generated, no describer supports it.', $stringType));
|
||||||
|
|
||||||
|
$registry = new ModelRegistry([], new Swagger());
|
||||||
|
$registry->register(new Model($type));
|
||||||
|
$registry->registerDefinitions();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function unsupportedTypesProvider()
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
[new Type(Type::BUILTIN_TYPE_ARRAY, false, null, true), 'mixed[]'],
|
||||||
|
[new Type(Type::BUILTIN_TYPE_OBJECT, false, self::class), self::class],
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
@ -17,6 +17,7 @@
|
|||||||
"require": {
|
"require": {
|
||||||
"php": "~7.0|~7.1",
|
"php": "~7.0|~7.1",
|
||||||
"symfony/framework-bundle": "^2.8|^3.0",
|
"symfony/framework-bundle": "^2.8|^3.0",
|
||||||
|
"symfony/property-info": "^2.8|^3.0",
|
||||||
"exsyst/swagger": "~0.2.3"
|
"exsyst/swagger": "~0.2.3"
|
||||||
},
|
},
|
||||||
"require-dev": {
|
"require-dev": {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user