mirror of
https://github.com/retailcrm/NelmioApiDocBundle.git
synced 2025-02-13 04:59:24 +03:00
Make the document generated valid
This commit is contained in:
parent
d1adffe41f
commit
02601125bd
@ -12,7 +12,7 @@
|
|||||||
namespace EXSyst\Bundle\ApiDocBundle;
|
namespace EXSyst\Bundle\ApiDocBundle;
|
||||||
|
|
||||||
use EXSyst\Bundle\ApiDocBundle\Describer\DescriberInterface;
|
use EXSyst\Bundle\ApiDocBundle\Describer\DescriberInterface;
|
||||||
use EXSyst\Swagger\Swagger;
|
use EXSyst\Component\Swagger\Swagger;
|
||||||
|
|
||||||
class ApiDocGenerator
|
class ApiDocGenerator
|
||||||
{
|
{
|
||||||
|
64
Describer/DefaultDescriber.php
Normal file
64
Describer/DefaultDescriber.php
Normal file
@ -0,0 +1,64 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This file is part of the ApiDocBundle package.
|
||||||
|
*
|
||||||
|
* (c) EXSyst
|
||||||
|
*
|
||||||
|
* For the full copyright and license information, please view the LICENSE
|
||||||
|
* file that was distributed with this source code.
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace EXSyst\Bundle\ApiDocBundle\Describer;
|
||||||
|
|
||||||
|
use EXSyst\Component\Swagger\Swagger;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Makes the swagger documentation valid even if there are missing fields.
|
||||||
|
*
|
||||||
|
* @author Ener-Getick <egetick@gmail.com>
|
||||||
|
*/
|
||||||
|
class DefaultDescriber implements DescriberInterface
|
||||||
|
{
|
||||||
|
public function describe(Swagger $api)
|
||||||
|
{
|
||||||
|
// Info
|
||||||
|
$info = $api->getInfo();
|
||||||
|
if (null === $info->getTitle()) {
|
||||||
|
$info->setTitle('');
|
||||||
|
}
|
||||||
|
if (null === $info->getVersion()) {
|
||||||
|
$info->setVersion('0.0.0');
|
||||||
|
}
|
||||||
|
|
||||||
|
// Paths
|
||||||
|
$paths = $api->getPaths();
|
||||||
|
foreach ($paths as $uri => $path) {
|
||||||
|
// Path Parameters
|
||||||
|
preg_match_all('/\{(.+)\}/SU', $uri, $matches);
|
||||||
|
$pathParameters = $matches[1];
|
||||||
|
|
||||||
|
foreach ($path->getMethods() as $method) {
|
||||||
|
$operation = $path->getOperation($method);
|
||||||
|
$parameters = $operation->getParameters();
|
||||||
|
|
||||||
|
// Default Path Parameters
|
||||||
|
foreach ($pathParameters as $pathParameter) {
|
||||||
|
if ($parameters->has($pathParameter, 'path')) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
$parameters->get($pathParameter, 'path')
|
||||||
|
->setRequired(true)
|
||||||
|
->setType('string');
|
||||||
|
}
|
||||||
|
|
||||||
|
// Default Response
|
||||||
|
if (0 === iterator_count($operation->getResponses())) {
|
||||||
|
$defaultResponse = $operation->getResponses()->get('default');
|
||||||
|
$defaultResponse->setDescription('');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -11,7 +11,7 @@
|
|||||||
|
|
||||||
namespace EXSyst\Bundle\ApiDocBundle\Describer;
|
namespace EXSyst\Bundle\ApiDocBundle\Describer;
|
||||||
|
|
||||||
use EXSyst\Swagger\Swagger;
|
use EXSyst\Component\Swagger\Swagger;
|
||||||
|
|
||||||
interface DescriberInterface
|
interface DescriberInterface
|
||||||
{
|
{
|
||||||
|
@ -11,7 +11,7 @@
|
|||||||
|
|
||||||
namespace EXSyst\Bundle\ApiDocBundle\Describer;
|
namespace EXSyst\Bundle\ApiDocBundle\Describer;
|
||||||
|
|
||||||
use EXSyst\Swagger\Swagger;
|
use EXSyst\Component\Swagger\Swagger;
|
||||||
|
|
||||||
class ExternalDocDescriber implements DescriberInterface
|
class ExternalDocDescriber implements DescriberInterface
|
||||||
{
|
{
|
||||||
|
@ -13,7 +13,7 @@ namespace EXSyst\Bundle\ApiDocBundle\Describer;
|
|||||||
|
|
||||||
use Doctrine\Common\Util\ClassUtils;
|
use Doctrine\Common\Util\ClassUtils;
|
||||||
use EXSyst\Bundle\ApiDocBundle\RouteDescriber\RouteDescriberInterface;
|
use EXSyst\Bundle\ApiDocBundle\RouteDescriber\RouteDescriberInterface;
|
||||||
use EXSyst\Swagger\Swagger;
|
use EXSyst\Component\Swagger\Swagger;
|
||||||
use Symfony\Bundle\FrameworkBundle\Controller\ControllerNameParser;
|
use Symfony\Bundle\FrameworkBundle\Controller\ControllerNameParser;
|
||||||
use Symfony\Component\DependencyInjection\ContainerInterface;
|
use Symfony\Component\DependencyInjection\ContainerInterface;
|
||||||
use Symfony\Component\Routing\Route;
|
use Symfony\Component\Routing\Route;
|
||||||
|
@ -16,9 +16,22 @@ class SwaggerPhpDescriber extends ExternalDocDescriber
|
|||||||
public function __construct(string $projectPath, bool $overwrite = false)
|
public function __construct(string $projectPath, bool $overwrite = false)
|
||||||
{
|
{
|
||||||
parent::__construct(function () use ($projectPath) {
|
parent::__construct(function () use ($projectPath) {
|
||||||
$annotation = \Swagger\scan($projectPath);
|
// Catch notices as the documentation can be completed by other describers
|
||||||
|
$prevHandler = set_error_handler(function ($type, $message, $file, $line, $context) use (&$prevHandler) {
|
||||||
|
if (E_USER_NOTICE === $type || E_USER_WARNING === $type) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
return json_decode(json_encode($annotation));
|
return null !== $prevHandler && call_user_func($prevHandler, $type, $message, $file, $line, $context);
|
||||||
|
});
|
||||||
|
|
||||||
|
try {
|
||||||
|
$annotation = \Swagger\scan($projectPath);
|
||||||
|
|
||||||
|
return json_decode(json_encode($annotation));
|
||||||
|
} finally {
|
||||||
|
restore_error_handler();
|
||||||
|
}
|
||||||
}, $overwrite);
|
}, $overwrite);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -24,6 +24,10 @@
|
|||||||
<tag name="exsyst_api_doc.describer" priority="-100" />
|
<tag name="exsyst_api_doc.describer" priority="-100" />
|
||||||
</service>
|
</service>
|
||||||
|
|
||||||
|
<service id="exsyst_api_doc.describers.default" class="EXSyst\Bundle\ApiDocBundle\Describer\DefaultDescriber" public="false">
|
||||||
|
<tag name="exsyst_api_doc.describer" priority="-1000" />
|
||||||
|
</service>
|
||||||
|
|
||||||
<!-- Routing Extractors -->
|
<!-- Routing Extractors -->
|
||||||
<service id="exsyst_api_doc.route_describers.route_metadata" class="EXSyst\Bundle\ApiDocBundle\RouteDescriber\RouteMetadataDescriber" public="false">
|
<service id="exsyst_api_doc.route_describers.route_metadata" class="EXSyst\Bundle\ApiDocBundle\RouteDescriber\RouteMetadataDescriber" public="false">
|
||||||
<tag name="exsyst_api_doc.route_describer" priority="-50" />
|
<tag name="exsyst_api_doc.route_describer" priority="-50" />
|
||||||
|
@ -12,8 +12,8 @@
|
|||||||
namespace EXSyst\Bundle\ApiDocBundle\RouteDescriber;
|
namespace EXSyst\Bundle\ApiDocBundle\RouteDescriber;
|
||||||
|
|
||||||
use Doctrine\Common\Annotations\Reader;
|
use Doctrine\Common\Annotations\Reader;
|
||||||
use EXSyst\Swagger\Parameter;
|
use EXSyst\Component\Swagger\Parameter;
|
||||||
use EXSyst\Swagger\Swagger;
|
use EXSyst\Component\Swagger\Swagger;
|
||||||
use Nelmio\ApiDocBundle\Annotation\ApiDoc;
|
use Nelmio\ApiDocBundle\Annotation\ApiDoc;
|
||||||
use Symfony\Component\Routing\Route;
|
use Symfony\Component\Routing\Route;
|
||||||
|
|
||||||
|
@ -11,7 +11,7 @@
|
|||||||
|
|
||||||
namespace EXSyst\Bundle\ApiDocBundle\RouteDescriber;
|
namespace EXSyst\Bundle\ApiDocBundle\RouteDescriber;
|
||||||
|
|
||||||
use EXSyst\Swagger\Swagger;
|
use EXSyst\Component\Swagger\Swagger;
|
||||||
use phpDocumentor\Reflection\DocBlockFactory;
|
use phpDocumentor\Reflection\DocBlockFactory;
|
||||||
use phpDocumentor\Reflection\DocBlockFactoryInterface;
|
use phpDocumentor\Reflection\DocBlockFactoryInterface;
|
||||||
use Symfony\Component\Routing\Route;
|
use Symfony\Component\Routing\Route;
|
||||||
@ -48,10 +48,14 @@ class PhpDocDescriber implements RouteDescriberInterface
|
|||||||
if (null !== $docBlock) {
|
if (null !== $docBlock) {
|
||||||
$operation->setSummary($docBlock->getSummary());
|
$operation->setSummary($docBlock->getSummary());
|
||||||
$operation->setDescription((string) $docBlock->getDescription());
|
$operation->setDescription((string) $docBlock->getDescription());
|
||||||
$operation->setDeprecated($operation->getDeprecated() || $docBlock->hasTag('deprecated'));
|
if ($docBlock->hasTag('deprecated')) {
|
||||||
|
$operation->setDeprecated(true);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if (null !== $classDocBlock) {
|
if (null !== $classDocBlock) {
|
||||||
$operation->setDeprecated($operation->getDeprecated() || $classDocBlock->hasTag('deprecated'));
|
if ($classDocBlock->hasTag('deprecated')) {
|
||||||
|
$operation->setDeprecated(true);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -11,7 +11,7 @@
|
|||||||
|
|
||||||
namespace EXSyst\Bundle\ApiDocBundle\RouteDescriber;
|
namespace EXSyst\Bundle\ApiDocBundle\RouteDescriber;
|
||||||
|
|
||||||
use EXSyst\Swagger\Swagger;
|
use EXSyst\Component\Swagger\Swagger;
|
||||||
use Symfony\Component\Routing\Route;
|
use Symfony\Component\Routing\Route;
|
||||||
|
|
||||||
interface RouteDescriberInterface
|
interface RouteDescriberInterface
|
||||||
|
@ -11,8 +11,8 @@
|
|||||||
|
|
||||||
namespace EXSyst\Bundle\ApiDocBundle\RouteDescriber;
|
namespace EXSyst\Bundle\ApiDocBundle\RouteDescriber;
|
||||||
|
|
||||||
use EXSyst\Swagger\Operation;
|
use EXSyst\Component\Swagger\Operation;
|
||||||
use EXSyst\Swagger\Swagger;
|
use EXSyst\Component\Swagger\Swagger;
|
||||||
use Symfony\Component\Routing\Route;
|
use Symfony\Component\Routing\Route;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -27,7 +27,7 @@ trait RouteDescriberTrait
|
|||||||
*/
|
*/
|
||||||
private function getOperations(Swagger $api, Route $route)
|
private function getOperations(Swagger $api, Route $route)
|
||||||
{
|
{
|
||||||
$path = $api->getPaths()->get($route->getPath());
|
$path = $api->getPaths()->get($this->normalizePath($route->getPath()));
|
||||||
$methods = $route->getMethods() ?: Swagger::$METHODS;
|
$methods = $route->getMethods() ?: Swagger::$METHODS;
|
||||||
foreach ($methods as $method) {
|
foreach ($methods as $method) {
|
||||||
$method = strtolower($method);
|
$method = strtolower($method);
|
||||||
@ -40,4 +40,13 @@ trait RouteDescriberTrait
|
|||||||
|
|
||||||
return $operations;
|
return $operations;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private function normalizePath(string $path)
|
||||||
|
{
|
||||||
|
if (substr($path, -10) === '.{_format}') {
|
||||||
|
$path = substr($path, 0, -10);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $path;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -11,7 +11,7 @@
|
|||||||
|
|
||||||
namespace EXSyst\Bundle\ApiDocBundle\RouteDescriber;
|
namespace EXSyst\Bundle\ApiDocBundle\RouteDescriber;
|
||||||
|
|
||||||
use EXSyst\Swagger\Swagger;
|
use EXSyst\Component\Swagger\Swagger;
|
||||||
use Symfony\Component\Routing\Route;
|
use Symfony\Component\Routing\Route;
|
||||||
|
|
||||||
class RouteMetadataDescriber implements RouteDescriberInterface
|
class RouteMetadataDescriber implements RouteDescriberInterface
|
||||||
|
27
Tests/Describer/AbstractDescriberTest.php
Normal file
27
Tests/Describer/AbstractDescriberTest.php
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This file is part of the ApiDocBundle package.
|
||||||
|
*
|
||||||
|
* (c) EXSyst
|
||||||
|
*
|
||||||
|
* For the full copyright and license information, please view the LICENSE
|
||||||
|
* file that was distributed with this source code.
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace EXSyst\Bundle\ApiDocBundle\Tests\Describer;
|
||||||
|
|
||||||
|
use EXSyst\Component\Swagger\Swagger;
|
||||||
|
|
||||||
|
abstract class AbstractDescriberTest extends \PHPUnit_Framework_TestCase
|
||||||
|
{
|
||||||
|
protected $describer;
|
||||||
|
|
||||||
|
protected function getSwaggerDoc(): Swagger
|
||||||
|
{
|
||||||
|
$api = new Swagger();
|
||||||
|
$this->describer->describe($api);
|
||||||
|
|
||||||
|
return $api;
|
||||||
|
}
|
||||||
|
}
|
32
Tests/Describer/SwaggerPhpDescriberTest.php
Normal file
32
Tests/Describer/SwaggerPhpDescriberTest.php
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This file is part of the ApiDocBundle package.
|
||||||
|
*
|
||||||
|
* (c) EXSyst
|
||||||
|
*
|
||||||
|
* For the full copyright and license information, please view the LICENSE
|
||||||
|
* file that was distributed with this source code.
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace EXSyst\Bundle\ApiDocBundle\Tests\Describer;
|
||||||
|
|
||||||
|
use EXSyst\Bundle\ApiDocBundle\Describer\SwaggerPhpDescriber;
|
||||||
|
use EXSyst\Component\Swagger\Swagger;
|
||||||
|
|
||||||
|
class SwaggerPhpDescriberTest extends AbstractDescriberTest
|
||||||
|
{
|
||||||
|
public function testDescribe()
|
||||||
|
{
|
||||||
|
$api = $this->getSwaggerDoc();
|
||||||
|
$info = $api->getInfo();
|
||||||
|
|
||||||
|
$this->assertEquals('My Awesome App', $info->getTitle());
|
||||||
|
$this->assertEquals('1.3', $info->getVersion());
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function setUp()
|
||||||
|
{
|
||||||
|
$this->describer = new SwaggerPhpDescriber(__DIR__.'/../Fixtures');
|
||||||
|
}
|
||||||
|
}
|
@ -24,7 +24,7 @@ class ApiController
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @Route("/nelmio", methods={"POST"})
|
* @Route("/nelmio/{foo}", methods={"POST"})
|
||||||
* @ApiDoc(
|
* @ApiDoc(
|
||||||
* description="This action is described."
|
* description="This action is described."
|
||||||
* )
|
* )
|
||||||
|
@ -22,7 +22,7 @@ class FunctionalTest extends WebTestCase
|
|||||||
$this->assertEquals(['https'], $operation->getSchemes());
|
$this->assertEquals(['https'], $operation->getSchemes());
|
||||||
$this->assertEmpty($operation->getSummary());
|
$this->assertEmpty($operation->getSummary());
|
||||||
$this->assertEmpty($operation->getDescription());
|
$this->assertEmpty($operation->getDescription());
|
||||||
$this->assertFalse($operation->getDeprecated());
|
$this->assertNull($operation->getDeprecated());
|
||||||
|
|
||||||
$parameters = $operation->getParameters();
|
$parameters = $operation->getParameters();
|
||||||
$this->assertTrue($parameters->has('user', 'path'));
|
$this->assertTrue($parameters->has('user', 'path'));
|
||||||
@ -35,10 +35,14 @@ class FunctionalTest extends WebTestCase
|
|||||||
|
|
||||||
public function testNelmioAction()
|
public function testNelmioAction()
|
||||||
{
|
{
|
||||||
$operation = $this->getOperation('/nelmio', 'post');
|
$operation = $this->getOperation('/nelmio/{foo}', 'post');
|
||||||
|
|
||||||
$this->assertEquals('This action is described.', $operation->getDescription());
|
$this->assertEquals('This action is described.', $operation->getDescription());
|
||||||
$this->assertFalse($operation->getDeprecated());
|
$this->assertFalse($operation->getDeprecated());
|
||||||
|
|
||||||
|
$foo = $operation->getParameters()->get('foo', 'path');
|
||||||
|
$this->assertTrue($foo->getRequired());
|
||||||
|
$this->assertEquals('string', $foo->getType());
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testDeprecatedAction()
|
public function testDeprecatedAction()
|
||||||
@ -50,13 +54,12 @@ class FunctionalTest extends WebTestCase
|
|||||||
$this->assertTrue($operation->getDeprecated());
|
$this->assertTrue($operation->getDeprecated());
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testSwaggerPhpInfo()
|
public function testApiPlatform()
|
||||||
{
|
{
|
||||||
$api = $this->getSwaggerDefinition();
|
$operation = $this->getOperation('/api/dummies', 'get');
|
||||||
$info = $api->getInfo();
|
$operation = $this->getOperation('/api/foo', 'get');
|
||||||
|
$operation = $this->getOperation('/api/foo', 'post');
|
||||||
$this->assertEquals('My Awesome App', $info->getTitle());
|
$operation = $this->getOperation('/api/dummies/{id}', 'get');
|
||||||
$this->assertEquals('1.3', $info->getVersion());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private function getSwaggerDefinition()
|
private function getSwaggerDefinition()
|
||||||
|
@ -11,7 +11,7 @@
|
|||||||
"require": {
|
"require": {
|
||||||
"php": "^7.0",
|
"php": "^7.0",
|
||||||
"symfony/framework-bundle": "^3.2",
|
"symfony/framework-bundle": "^3.2",
|
||||||
"exsyst/swagger": "~0.1"
|
"exsyst/swagger": "dev-master"
|
||||||
},
|
},
|
||||||
"require-dev": {
|
"require-dev": {
|
||||||
"symfony/validator": "^3.2",
|
"symfony/validator": "^3.2",
|
||||||
@ -24,7 +24,7 @@
|
|||||||
"phpdocumentor/reflection-docblock": "^3.1",
|
"phpdocumentor/reflection-docblock": "^3.1",
|
||||||
"phpunit/phpunit": "^5.4",
|
"phpunit/phpunit": "^5.4",
|
||||||
"zircote/swagger-php": "^2.0",
|
"zircote/swagger-php": "^2.0",
|
||||||
"api-platform/core": "^2.0"
|
"api-platform/core": "dev-master"
|
||||||
},
|
},
|
||||||
"suggest": {
|
"suggest": {
|
||||||
"nelmio/api-doc-bundle": "For using the ApiDoc annotation.",
|
"nelmio/api-doc-bundle": "For using the ApiDoc annotation.",
|
||||||
|
Loading…
x
Reference in New Issue
Block a user