mirror of
https://github.com/retailcrm/NelmioApiDocBundle.git
synced 2025-02-02 23:59:26 +03:00
Add php docs support
This commit is contained in:
parent
42a2aefb41
commit
3490b01d1d
@ -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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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');
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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) {
|
||||||
|
61
Extractor/Routing/PhpDocExtractor.php
Normal file
61
Extractor/Routing/PhpDocExtractor.php
Normal 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'));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -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>
|
||||||
|
|
||||||
|
@ -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()
|
||||||
|
{
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -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);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -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": {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user