Make the document generated valid

This commit is contained in:
Ener-Getick 2016-08-01 19:58:57 +02:00
parent d1adffe41f
commit 02601125bd
No known key found for this signature in database
GPG Key ID: 9E5D2DB67BF054DD
18 changed files with 183 additions and 27 deletions

View File

@ -12,7 +12,7 @@
namespace EXSyst\Bundle\ApiDocBundle;
use EXSyst\Bundle\ApiDocBundle\Describer\DescriberInterface;
use EXSyst\Swagger\Swagger;
use EXSyst\Component\Swagger\Swagger;
class ApiDocGenerator
{

View 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('');
}
}
}
}
}

View File

@ -11,7 +11,7 @@
namespace EXSyst\Bundle\ApiDocBundle\Describer;
use EXSyst\Swagger\Swagger;
use EXSyst\Component\Swagger\Swagger;
interface DescriberInterface
{

View File

@ -11,7 +11,7 @@
namespace EXSyst\Bundle\ApiDocBundle\Describer;
use EXSyst\Swagger\Swagger;
use EXSyst\Component\Swagger\Swagger;
class ExternalDocDescriber implements DescriberInterface
{

View File

@ -13,7 +13,7 @@ namespace EXSyst\Bundle\ApiDocBundle\Describer;
use Doctrine\Common\Util\ClassUtils;
use EXSyst\Bundle\ApiDocBundle\RouteDescriber\RouteDescriberInterface;
use EXSyst\Swagger\Swagger;
use EXSyst\Component\Swagger\Swagger;
use Symfony\Bundle\FrameworkBundle\Controller\ControllerNameParser;
use Symfony\Component\DependencyInjection\ContainerInterface;
use Symfony\Component\Routing\Route;

View File

@ -16,9 +16,22 @@ class SwaggerPhpDescriber extends ExternalDocDescriber
public function __construct(string $projectPath, bool $overwrite = false)
{
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);
}
}

View File

@ -24,6 +24,10 @@
<tag name="exsyst_api_doc.describer" priority="-100" />
</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 -->
<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" />

View File

@ -12,8 +12,8 @@
namespace EXSyst\Bundle\ApiDocBundle\RouteDescriber;
use Doctrine\Common\Annotations\Reader;
use EXSyst\Swagger\Parameter;
use EXSyst\Swagger\Swagger;
use EXSyst\Component\Swagger\Parameter;
use EXSyst\Component\Swagger\Swagger;
use Nelmio\ApiDocBundle\Annotation\ApiDoc;
use Symfony\Component\Routing\Route;

View File

@ -11,7 +11,7 @@
namespace EXSyst\Bundle\ApiDocBundle\RouteDescriber;
use EXSyst\Swagger\Swagger;
use EXSyst\Component\Swagger\Swagger;
use phpDocumentor\Reflection\DocBlockFactory;
use phpDocumentor\Reflection\DocBlockFactoryInterface;
use Symfony\Component\Routing\Route;
@ -48,10 +48,14 @@ class PhpDocDescriber implements RouteDescriberInterface
if (null !== $docBlock) {
$operation->setSummary($docBlock->getSummary());
$operation->setDescription((string) $docBlock->getDescription());
$operation->setDeprecated($operation->getDeprecated() || $docBlock->hasTag('deprecated'));
if ($docBlock->hasTag('deprecated')) {
$operation->setDeprecated(true);
}
}
if (null !== $classDocBlock) {
$operation->setDeprecated($operation->getDeprecated() || $classDocBlock->hasTag('deprecated'));
if ($classDocBlock->hasTag('deprecated')) {
$operation->setDeprecated(true);
}
}
}
}

View File

@ -11,7 +11,7 @@
namespace EXSyst\Bundle\ApiDocBundle\RouteDescriber;
use EXSyst\Swagger\Swagger;
use EXSyst\Component\Swagger\Swagger;
use Symfony\Component\Routing\Route;
interface RouteDescriberInterface

View File

@ -11,8 +11,8 @@
namespace EXSyst\Bundle\ApiDocBundle\RouteDescriber;
use EXSyst\Swagger\Operation;
use EXSyst\Swagger\Swagger;
use EXSyst\Component\Swagger\Operation;
use EXSyst\Component\Swagger\Swagger;
use Symfony\Component\Routing\Route;
/**
@ -27,7 +27,7 @@ trait RouteDescriberTrait
*/
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;
foreach ($methods as $method) {
$method = strtolower($method);
@ -40,4 +40,13 @@ trait RouteDescriberTrait
return $operations;
}
private function normalizePath(string $path)
{
if (substr($path, -10) === '.{_format}') {
$path = substr($path, 0, -10);
}
return $path;
}
}

View File

@ -11,7 +11,7 @@
namespace EXSyst\Bundle\ApiDocBundle\RouteDescriber;
use EXSyst\Swagger\Swagger;
use EXSyst\Component\Swagger\Swagger;
use Symfony\Component\Routing\Route;
class RouteMetadataDescriber implements RouteDescriberInterface

View 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;
}
}

View 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');
}
}

View File

@ -24,7 +24,7 @@ class ApiController
}
/**
* @Route("/nelmio", methods={"POST"})
* @Route("/nelmio/{foo}", methods={"POST"})
* @ApiDoc(
* description="This action is described."
* )

View File

@ -22,7 +22,7 @@ class FunctionalTest extends WebTestCase
$this->assertEquals(['https'], $operation->getSchemes());
$this->assertEmpty($operation->getSummary());
$this->assertEmpty($operation->getDescription());
$this->assertFalse($operation->getDeprecated());
$this->assertNull($operation->getDeprecated());
$parameters = $operation->getParameters();
$this->assertTrue($parameters->has('user', 'path'));
@ -35,10 +35,14 @@ class FunctionalTest extends WebTestCase
public function testNelmioAction()
{
$operation = $this->getOperation('/nelmio', 'post');
$operation = $this->getOperation('/nelmio/{foo}', 'post');
$this->assertEquals('This action is described.', $operation->getDescription());
$this->assertFalse($operation->getDeprecated());
$foo = $operation->getParameters()->get('foo', 'path');
$this->assertTrue($foo->getRequired());
$this->assertEquals('string', $foo->getType());
}
public function testDeprecatedAction()
@ -50,13 +54,12 @@ class FunctionalTest extends WebTestCase
$this->assertTrue($operation->getDeprecated());
}
public function testSwaggerPhpInfo()
public function testApiPlatform()
{
$api = $this->getSwaggerDefinition();
$info = $api->getInfo();
$this->assertEquals('My Awesome App', $info->getTitle());
$this->assertEquals('1.3', $info->getVersion());
$operation = $this->getOperation('/api/dummies', 'get');
$operation = $this->getOperation('/api/foo', 'get');
$operation = $this->getOperation('/api/foo', 'post');
$operation = $this->getOperation('/api/dummies/{id}', 'get');
}
private function getSwaggerDefinition()

View File

@ -11,7 +11,7 @@
"require": {
"php": "^7.0",
"symfony/framework-bundle": "^3.2",
"exsyst/swagger": "~0.1"
"exsyst/swagger": "dev-master"
},
"require-dev": {
"symfony/validator": "^3.2",
@ -24,7 +24,7 @@
"phpdocumentor/reflection-docblock": "^3.1",
"phpunit/phpunit": "^5.4",
"zircote/swagger-php": "^2.0",
"api-platform/core": "^2.0"
"api-platform/core": "dev-master"
},
"suggest": {
"nelmio/api-doc-bundle": "For using the ApiDoc annotation.",