Add php docs support

This commit is contained in:
Ener-Getick 2016-07-13 23:05:14 +02:00
parent 42a2aefb41
commit 3490b01d1d
No known key found for this signature in database
GPG Key ID: 9E5D2DB67BF054DD
8 changed files with 151 additions and 27 deletions

View File

@ -16,6 +16,7 @@ use gossi\swagger\Swagger;
class ApiDocGenerator class ApiDocGenerator
{ {
private $swagger;
private $extractors; private $extractors;
/** /**
@ -31,11 +32,15 @@ class ApiDocGenerator
*/ */
public function extract() public function extract()
{ {
$swagger = new Swagger(); if (null !== $this->swagger) {
foreach ($this->extractors as $extractor) { return $this->swagger;
$extractor->extractIn($swagger);
} }
return $swagger; $this->swagger = new Swagger();
foreach ($this->extractors as $extractor) {
$extractor->extractIn($this->swagger);
}
return $this->swagger;
} }
} }

View File

@ -11,6 +11,8 @@
namespace EXSyst\Bundle\ApiDocBundle\DependencyInjection; namespace EXSyst\Bundle\ApiDocBundle\DependencyInjection;
use Nelmio\ApiDocBundle\Annotation\ApiDoc;
use phpDocumentor\Reflection\DocBlockFactory;
use Symfony\Component\Config\FileLocator; use Symfony\Component\Config\FileLocator;
use Symfony\Component\HttpKernel\DependencyInjection\Extension; use Symfony\Component\HttpKernel\DependencyInjection\Extension;
use Symfony\Component\DependencyInjection\Loader\XmlFileLoader; use Symfony\Component\DependencyInjection\Loader\XmlFileLoader;
@ -35,5 +37,13 @@ class EXSystApiDocExtension extends Extension
$loader = new XmlFileLoader($container, new FileLocator(__DIR__.'/../Resources/config')); $loader = new XmlFileLoader($container, new FileLocator(__DIR__.'/../Resources/config'));
$loader->load('services.xml'); $loader->load('services.xml');
// Removes useless services
if (!class_exists(ApiDoc::class)) {
$container->removeDefinition('exsyst_api_doc.routing_extractors.nelmio_annotation');
}
if (!class_exists(DocBlockFactory::class)) {
$container->removeDefinition('exsyst_api_doc.routing_extractors.php_doc');
}
} }
} }

View File

@ -22,20 +22,14 @@ class NelmioAnnotationExtractor implements RouteExtractorInterface
use RouteExtractorTrait; use RouteExtractorTrait;
private $annotationReader; private $annotationReader;
private $nelmioLoaded;
public function __construct(Reader $annotationReader) public function __construct(Reader $annotationReader)
{ {
$this->annotationReader = $annotationReader; $this->annotationReader = $annotationReader;
$this->nelmioLoaded = class_exists(ApiDoc::class);
} }
public function extractIn(Swagger $api, Route $route, \ReflectionMethod $reflectionMethod) public function extractIn(Swagger $api, Route $route, \ReflectionMethod $reflectionMethod)
{ {
if (!$this->nelmioLoaded) {
return;
}
$annotation = $this->annotationReader->getMethodAnnotation($reflectionMethod, ApiDoc::class); $annotation = $this->annotationReader->getMethodAnnotation($reflectionMethod, ApiDoc::class);
if (null === $annotation) { if (null === $annotation) {
return; return;
@ -43,14 +37,12 @@ class NelmioAnnotationExtractor implements RouteExtractorInterface
// some fields aren't available otherwise // some fields aren't available otherwise
$annotationArray = $annotation->toArray(); $annotationArray = $annotation->toArray();
foreach ($this->getOperations($api, $route) as $operation) { foreach ($this->getOperations($api, $route) as $operation) {
if ($annotation->getDescription()) { if ($annotation->getDescription()) {
$operation->setDescription($annotation->getDescription()); $operation->setDescription($annotation->getDescription());
} }
if (null !== $annotation->getDeprecated()) { $operation->setDeprecated($operation->getDeprecated() || $annotation->getDeprecated());
$operation->setDeprecated($operation->getDeprecated || $annotation->getDeprecated());
}
// Request parameters // Request parameters
foreach ($annotation->getParameters() as $name => $configuration) { foreach ($annotation->getParameters() as $name => $configuration) {

View File

@ -0,0 +1,61 @@
<?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\Extractor\Routing;
use phpDocumentor\Reflection\DocBlockFactory;
use phpDocumentor\Reflection\DocBlockFactoryInterface;
use Doctrine\Common\Annotations\Reader;
use gossi\swagger\Parameter;
use gossi\swagger\Swagger;
use Nelmio\ApiDocBundle\Annotation\ApiDoc;
use Symfony\Component\Routing\Route;
class PhpDocExtractor implements RouteExtractorInterface
{
use RouteExtractorTrait;
private $docBlockFactory;
public function __construct(DocBlockFactoryInterface $docBlockFactory = null)
{
if (null === $docBlockFactory) {
$docBlockFactory = DocBlockFactory::createInstance();
}
$this->docBlockFactory = $docBlockFactory;
}
public function extractIn(Swagger $api, Route $route, \ReflectionMethod $reflectionMethod)
{
$classDocBlock = null;
$docBlock = null;
try {
$classDocBlock = $this->docBlockFactory->create($reflectionMethod->getDeclaringClass());
} catch (\Exception $e) {
}
try {
$docBlock = $this->docBlockFactory->create($reflectionMethod);
} catch (\Exception $e) {
}
foreach ($this->getOperations($api, $route) as $operation) {
if (null !== $docBlock) {
$operation->setSummary($docBlock->getSummary());
$operation->setDescription((string) $docBlock->getDescription());
$operation->setDeprecated($operation->getDeprecated() || $docBlock->hasTag('deprecated'));
}
if (null !== $classDocBlock) {
$operation->setDeprecated($operation->getDeprecated() || $classDocBlock->hasTag('deprecated'));
}
}
}
}

View File

@ -19,13 +19,17 @@
<!-- Routing Extractors --> <!-- Routing Extractors -->
<service id="exsyst_api_doc.routing_extractors.route_metadata" class="EXSyst\Bundle\ApiDocBundle\Extractor\Routing\RouteMetadataExtractor" public="false"> <service id="exsyst_api_doc.routing_extractors.route_metadata" class="EXSyst\Bundle\ApiDocBundle\Extractor\Routing\RouteMetadataExtractor" public="false">
<tag name="exsyst_api_doc.routing_extractor" /> <tag name="exsyst_api_doc.routing_extractor" priority="-50" />
</service>
<service id="exsyst_api_doc.routing_extractors.php_doc" class="EXSyst\Bundle\ApiDocBundle\Extractor\Routing\PhpDocExtractor" public="false">
<tag name="exsyst_api_doc.routing_extractor" priority="-70" />
</service> </service>
<service id="exsyst_api_doc.routing_extractors.nelmio_annotation" class="EXSyst\Bundle\ApiDocBundle\Extractor\Routing\NelmioAnnotationExtractor" public="false"> <service id="exsyst_api_doc.routing_extractors.nelmio_annotation" class="EXSyst\Bundle\ApiDocBundle\Extractor\Routing\NelmioAnnotationExtractor" public="false">
<argument type="service" id="annotation_reader" /> <argument type="service" id="annotation_reader" />
<tag name="exsyst_api_doc.routing_extractor" /> <tag name="exsyst_api_doc.routing_extractor" priority="-100" />
</service> </service>
</services> </services>

View File

@ -11,6 +11,7 @@
namespace EXSyst\Bundle\ApiDocBundle\Tests\Functional\Controller; namespace EXSyst\Bundle\ApiDocBundle\Tests\Functional\Controller;
use Nelmio\ApiDocBundle\Annotation\ApiDoc;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route; use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route;
class ApiController class ApiController
@ -21,4 +22,26 @@ class ApiController
public function userAction() public function userAction()
{ {
} }
/**
* @Route("/nelmio", methods={"POST"})
* @ApiDoc(
* description="This action is described."
* )
*/
public function nelmioAction()
{
}
/**
* This action is deprecated.
*
* Please do not use this action.
*
* @Route("/deprecated", methods={"GET"})
* @deprecated
*/
public function deprecatedAction()
{
}
} }

View File

@ -15,18 +15,14 @@ use Symfony\Bundle\FrameworkBundle\Test\WebTestCase;
class FunctionalTest extends WebTestCase class FunctionalTest extends WebTestCase
{ {
public function testUserActionApiController() public function testUserAction()
{ {
$api = $this->getSwaggerDefinition(); $operation = $this->getOperation('/test/{user}', 'get');
$paths = $api->getPaths();
$this->assertTrue($paths->has('/test/{user}'));
$action = $paths->get('/test/{user}');
$this->assertTrue($action->hasOperation('get'));
$operation = $action->getOperation('get');
$this->assertEquals(['https'], $operation->getSchemes()->toArray()); $this->assertEquals(['https'], $operation->getSchemes()->toArray());
$this->assertEmpty($operation->getSummary());
$this->assertEmpty($operation->getDescription());
$this->assertFalse($operation->getDeprecated());
$parameters = $operation->getParameters(); $parameters = $operation->getParameters();
$this->assertTrue($parameters->search('user', 'path')); $this->assertTrue($parameters->search('user', 'path'));
@ -37,10 +33,40 @@ class FunctionalTest extends WebTestCase
$this->assertEquals('/foo/', $parameter->getFormat()); $this->assertEquals('/foo/', $parameter->getFormat());
} }
public function testNelmioAction()
{
$operation = $this->getOperation('/nelmio', 'post');
$this->assertEquals('This action is described.', $operation->getDescription());
$this->assertFalse($operation->getDeprecated());
}
public function testDeprecatedAction()
{
$operation = $this->getOperation('/deprecated', 'get');
$this->assertEquals('This action is deprecated.', $operation->getSummary());
$this->assertEquals('Please do not use this action.', $operation->getDescription());
$this->assertTrue($operation->getDeprecated());
}
private function getSwaggerDefinition() private function getSwaggerDefinition()
{ {
static::createClient(); static::createClient();
return static::$kernel->getContainer()->get('exsyst_api_doc.generator')->extract(); return static::$kernel->getContainer()->get('exsyst_api_doc.generator')->extract();
} }
private function getOperation($path, $method)
{
$api = $this->getSwaggerDefinition();
$paths = $api->getPaths();
$this->assertTrue($paths->has($path));
$action = $paths->get($path);
$this->assertTrue($action->hasOperation($method));
return $action->getOperation($method);
}
} }

View File

@ -17,10 +17,13 @@
"symfony/browser-kit": "~2.8|~3.0", "symfony/browser-kit": "~2.8|~3.0",
"symfony/phpunit-bridge": "^3.2@dev", "symfony/phpunit-bridge": "^3.2@dev",
"sensio/framework-extra-bundle": "~3.0", "sensio/framework-extra-bundle": "~3.0",
"nelmio/api-doc-bundle": "^2.0" "nelmio/api-doc-bundle": "^2.0",
"phpdocumentor/reflection-docblock": "^3.1",
"phpunit/phpunit": "^5.4"
}, },
"suggest": { "suggest": {
"nelmio/api-doc-bundle": "For using the ApiDoc annotation." "nelmio/api-doc-bundle": "For using the ApiDoc annotation.",
"phpdocumentor/reflection-docblock": "For parsing php docs."
}, },
"autoload": { "autoload": {
"psr-4": { "psr-4": {