mirror of
https://github.com/retailcrm/NelmioApiDocBundle.git
synced 2025-02-02 23:59:26 +03:00
Add FOSRestBundle support
This commit is contained in:
parent
7a7f78a53f
commit
ffda7801f6
@ -39,15 +39,15 @@ class EXSystApiDocExtension extends Extension
|
|||||||
|
|
||||||
$loader->load('services.xml');
|
$loader->load('services.xml');
|
||||||
|
|
||||||
// Removes useless services
|
// Import services needed for each library
|
||||||
if (!class_exists(ApiDoc::class)) {
|
if (class_exists(ApiDoc::class)) {
|
||||||
$container->removeDefinition('exsyst_api_doc.route_describers.nelmio_annotation');
|
$loader->load('nelmio_apidoc.xml');
|
||||||
}
|
}
|
||||||
if (!class_exists(DocBlockFactory::class)) {
|
if (class_exists(DocBlockFactory::class)) {
|
||||||
$container->removeDefinition('exsyst_api_doc.route_describers.php_doc');
|
$loader->load('php_doc.xml');
|
||||||
}
|
}
|
||||||
if (!class_exists(Swagger::class)) {
|
if (class_exists(Swagger::class)) {
|
||||||
$container->removeDefinition('exsyst_api_doc.describers.swagger_php');
|
$loader->load('swagger_php.xml');
|
||||||
}
|
}
|
||||||
|
|
||||||
$bundles = $container->getParameter('kernel.bundles');
|
$bundles = $container->getParameter('kernel.bundles');
|
||||||
|
@ -34,24 +34,8 @@ class DefaultDescriber implements DescriberInterface
|
|||||||
// Paths
|
// Paths
|
||||||
$paths = $api->getPaths();
|
$paths = $api->getPaths();
|
||||||
foreach ($paths as $uri => $path) {
|
foreach ($paths as $uri => $path) {
|
||||||
// Path Parameters
|
|
||||||
preg_match_all('/\{(.+)\}/SU', $uri, $matches);
|
|
||||||
$pathParameters = $matches[1];
|
|
||||||
|
|
||||||
foreach ($path->getMethods() as $method) {
|
foreach ($path->getMethods() as $method) {
|
||||||
$operation = $path->getOperation($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
|
// Default Response
|
||||||
if (0 === iterator_count($operation->getResponses())) {
|
if (0 === iterator_count($operation->getResponses())) {
|
||||||
|
@ -8,7 +8,7 @@
|
|||||||
<argument type="service" id="exsyst_api_doc.describers.api_platform.documentation" />
|
<argument type="service" id="exsyst_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="exsyst_api_doc.describer" priority="-150" />
|
<tag name="exsyst_api_doc.describer" priority="-200" />
|
||||||
</service>
|
</service>
|
||||||
|
|
||||||
<service id="exsyst_api_doc.describers.api_platform.documentation" class="ApiPlatform\Core\Documentation\Documentation" public="false">
|
<service id="exsyst_api_doc.describers.api_platform.documentation" class="ApiPlatform\Core\Documentation\Documentation" public="false">
|
||||||
|
14
Resources/config/nelmio_apidoc.xml
Normal file
14
Resources/config/nelmio_apidoc.xml
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
<?xml version="1.0" ?>
|
||||||
|
<container xmlns="http://symfony.com/schema/dic/services"
|
||||||
|
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||||
|
xsi:schemaLocation="http://symfony.com/schema/dic/services http://symfony.com/schema/dic/services/services-1.0.xsd">
|
||||||
|
|
||||||
|
<services>
|
||||||
|
<service id="exsyst_api_doc.route_describers.nelmio_annotation" class="EXSyst\Bundle\ApiDocBundle\RouteDescriber\NelmioAnnotationDescriber" public="false">
|
||||||
|
<argument type="service" id="annotation_reader" />
|
||||||
|
|
||||||
|
<tag name="exsyst_api_doc.route_describer" priority="-250" />
|
||||||
|
</service>
|
||||||
|
</services>
|
||||||
|
|
||||||
|
</container>
|
12
Resources/config/php_doc.xml
Normal file
12
Resources/config/php_doc.xml
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
<?xml version="1.0" ?>
|
||||||
|
<container xmlns="http://symfony.com/schema/dic/services"
|
||||||
|
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||||
|
xsi:schemaLocation="http://symfony.com/schema/dic/services http://symfony.com/schema/dic/services/services-1.0.xsd">
|
||||||
|
|
||||||
|
<services>
|
||||||
|
<service id="exsyst_api_doc.route_describers.php_doc" class="EXSyst\Bundle\ApiDocBundle\RouteDescriber\PhpDocDescriber" public="false">
|
||||||
|
<tag name="exsyst_api_doc.route_describer" priority="-1000" />
|
||||||
|
</service>
|
||||||
|
</services>
|
||||||
|
|
||||||
|
</container>
|
@ -9,12 +9,6 @@
|
|||||||
</service>
|
</service>
|
||||||
|
|
||||||
<!-- Extractors -->
|
<!-- Extractors -->
|
||||||
<service id="exsyst_api_doc.describers.swagger_php" class="EXSyst\Bundle\ApiDocBundle\Describer\SwaggerPhpDescriber" public="false">
|
|
||||||
<argument>%kernel.root_dir%</argument>
|
|
||||||
|
|
||||||
<tag name="exsyst_api_doc.describer" priority="-150" />
|
|
||||||
</service>
|
|
||||||
|
|
||||||
<service id="exsyst_api_doc.describers.route" class="EXSyst\Bundle\ApiDocBundle\Describer\RouteDescriber" public="false">
|
<service id="exsyst_api_doc.describers.route" class="EXSyst\Bundle\ApiDocBundle\Describer\RouteDescriber" public="false">
|
||||||
<argument type="service" id="service_container" />
|
<argument type="service" id="service_container" />
|
||||||
<argument type="service" id="router" />
|
<argument type="service" id="router" />
|
||||||
@ -30,16 +24,6 @@
|
|||||||
|
|
||||||
<!-- 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" />
|
|
||||||
</service>
|
|
||||||
|
|
||||||
<service id="exsyst_api_doc.route_describers.php_doc" class="EXSyst\Bundle\ApiDocBundle\RouteDescriber\PhpDocDescriber" public="false">
|
|
||||||
<tag name="exsyst_api_doc.route_describer" priority="-70" />
|
|
||||||
</service>
|
|
||||||
|
|
||||||
<service id="exsyst_api_doc.route_describers.nelmio_annotation" class="EXSyst\Bundle\ApiDocBundle\RouteDescriber\NelmioAnnotationDescriber" public="false">
|
|
||||||
<argument type="service" id="annotation_reader" />
|
|
||||||
|
|
||||||
<tag name="exsyst_api_doc.route_describer" priority="-100" />
|
<tag name="exsyst_api_doc.route_describer" priority="-100" />
|
||||||
</service>
|
</service>
|
||||||
</services>
|
</services>
|
||||||
|
14
Resources/config/swagger_php.xml
Normal file
14
Resources/config/swagger_php.xml
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
<?xml version="1.0" ?>
|
||||||
|
<container xmlns="http://symfony.com/schema/dic/services"
|
||||||
|
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||||
|
xsi:schemaLocation="http://symfony.com/schema/dic/services http://symfony.com/schema/dic/services/services-1.0.xsd">
|
||||||
|
|
||||||
|
<services>
|
||||||
|
<service id="exsyst_api_doc.describers.swagger_php" class="EXSyst\Bundle\ApiDocBundle\Describer\SwaggerPhpDescriber" public="false">
|
||||||
|
<argument>%kernel.root_dir%</argument>
|
||||||
|
|
||||||
|
<tag name="exsyst_api_doc.describer" priority="-300" />
|
||||||
|
</service>
|
||||||
|
</services>
|
||||||
|
|
||||||
|
</container>
|
82
RouteDescriber/FosRestDescriber.php
Normal file
82
RouteDescriber/FosRestDescriber.php
Normal file
@ -0,0 +1,82 @@
|
|||||||
|
<?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\RouteDescriber;
|
||||||
|
|
||||||
|
use Doctrine\Common\Annotations\Reader;
|
||||||
|
use EXSyst\Component\Swagger\Parameter;
|
||||||
|
use EXSyst\Component\Swagger\Swagger;
|
||||||
|
use FOS\RestBundle\Controller\Annotations\RequestParam;
|
||||||
|
use FOS\RestBundle\Controller\Annotations\QueryParam;
|
||||||
|
use Nelmio\ApiDocBundle\Annotation\ApiDoc;
|
||||||
|
use Symfony\Component\Routing\Route;
|
||||||
|
use Symfony\Component\Validator\Constraint;
|
||||||
|
use Symfony\Component\Validator\Constraints\Regex;
|
||||||
|
|
||||||
|
class FosRestDescriber implements RouteDescriberInterface
|
||||||
|
{
|
||||||
|
use RouteDescriberTrait;
|
||||||
|
|
||||||
|
private $annotationReader;
|
||||||
|
|
||||||
|
public function __construct(Reader $annotationReader)
|
||||||
|
{
|
||||||
|
$this->annotationReader = $annotationReader;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function describe(Swagger $api, Route $route, \ReflectionMethod $reflectionMethod)
|
||||||
|
{
|
||||||
|
$annotations = $this->annotationReader->getMethodAnnotations();
|
||||||
|
$annotations = array_filter($annotations, function ($value) {
|
||||||
|
return $value instanceof RequestParam || $value instanceof QueryParam;
|
||||||
|
});
|
||||||
|
|
||||||
|
foreach ($this->getOperations($api, $route) as $operation) {
|
||||||
|
foreach ($annotations as $annotation) {
|
||||||
|
$in = $annotation instanceof QueryParam ? 'query' : 'formData';
|
||||||
|
$parameter = $operation->getParameters()->get($annotation->getKey(), $in);
|
||||||
|
|
||||||
|
$parameter->setAllowEmptyValue($annotation->nullable && $annotation->allowBlank);
|
||||||
|
$parameter->setType($annotation->map ? 'array' : 'string');
|
||||||
|
$parameter->setDefault($annotation->getDefault());
|
||||||
|
if (null === $parameter->getDescription()) {
|
||||||
|
$parameter->setDescription($annotation->description);
|
||||||
|
}
|
||||||
|
|
||||||
|
$normalizedRequirements = $this->normalizeRequirements($annotation->requirements);
|
||||||
|
if (null !== $normalizedRequirements) {
|
||||||
|
$parameter->setFormat($normalizedRequirements);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private function normalizeRequirements($requirements)
|
||||||
|
{
|
||||||
|
// if pattern
|
||||||
|
if (isset($requirements['rule'])) {
|
||||||
|
return (string) $requirements['rule'];
|
||||||
|
}
|
||||||
|
if (is_string($requirements)) {
|
||||||
|
return $requirements;
|
||||||
|
}
|
||||||
|
// if custom constraint
|
||||||
|
if ($requirements instanceof Constraint) {
|
||||||
|
if ($requirements instanceof Regex) {
|
||||||
|
return $requirements->getHtmlPattern();
|
||||||
|
}
|
||||||
|
|
||||||
|
$reflectionClass = new \ReflectionClass($requirements);
|
||||||
|
|
||||||
|
return $reflectionClass->getShortName();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -39,28 +39,41 @@ class NelmioAnnotationDescriber implements RouteDescriberInterface
|
|||||||
$annotationArray = $annotation->toArray();
|
$annotationArray = $annotation->toArray();
|
||||||
|
|
||||||
foreach ($this->getOperations($api, $route) as $operation) {
|
foreach ($this->getOperations($api, $route) as $operation) {
|
||||||
if ($annotation->getDescription()) {
|
if (null === $operation->getDescription()) {
|
||||||
$operation->setDescription($annotation->getDescription());
|
$operation->setDescription($annotation->getDescription());
|
||||||
}
|
}
|
||||||
$operation->setDeprecated($operation->getDeprecated() || $annotation->getDeprecated());
|
if (null === $operation->getDeprecated() && $annotation->getDeprecated()) {
|
||||||
|
$operation->setDeprecated(true);
|
||||||
|
}
|
||||||
|
|
||||||
// Request parameters
|
// Request parameters
|
||||||
foreach ($annotation->getParameters() as $name => $configuration) {
|
foreach ($annotation->getParameters() as $name => $configuration) {
|
||||||
$parameter = $operation->getParameters()->get($name, 'formData');
|
$parameter = $operation->getParameters()->get($name, 'formData');
|
||||||
if (isset($configuration['required'])) {
|
if (isset($configuration['required']) && $configuration['required']) {
|
||||||
$parameter->setRequired($parameter->getRequired() || $configuration['required']);
|
$parameter->setRequired(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
$this->configureParameter($parameter, $configuration);
|
$this->configureParameter($parameter, $configuration);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Query parameters
|
// Query/Path required parameters
|
||||||
|
$compiledRoute = $route->compile();
|
||||||
|
$pathVariables = $compiledRoute->getVariables();
|
||||||
|
$hostVariables = $compiledRoute->getHostVariables();
|
||||||
foreach ($annotation->getRequirements() as $name => $configuration) {
|
foreach ($annotation->getRequirements() as $name => $configuration) {
|
||||||
$parameter = $operation->getParameters()->get($name, 'query');
|
if (in_array($name, $pathVariables)) {
|
||||||
|
$in = 'path';
|
||||||
|
} elseif (!in_array($name, $hostVariables)) {
|
||||||
|
$in = 'query';
|
||||||
|
} else { // Host variables not supported
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
$parameter = $operation->getParameters()->get($name, $in);
|
||||||
$parameter->setRequired(true);
|
$parameter->setRequired(true);
|
||||||
|
|
||||||
$this->configureParameter($parameter, $configuration);
|
$this->configureParameter($parameter, $configuration);
|
||||||
}
|
}
|
||||||
|
// Optional Query parameters
|
||||||
foreach ($annotation->getFilters() as $name => $configuration) {
|
foreach ($annotation->getFilters() as $name => $configuration) {
|
||||||
$parameter = $operation->getParameters()->get($name, 'query');
|
$parameter = $operation->getParameters()->get($name, 'query');
|
||||||
$this->configureParameter($parameter, $configuration);
|
$this->configureParameter($parameter, $configuration);
|
||||||
|
@ -46,8 +46,12 @@ class PhpDocDescriber implements RouteDescriberInterface
|
|||||||
|
|
||||||
foreach ($this->getOperations($api, $route) as $operation) {
|
foreach ($this->getOperations($api, $route) as $operation) {
|
||||||
if (null !== $docBlock) {
|
if (null !== $docBlock) {
|
||||||
$operation->setSummary($docBlock->getSummary());
|
if (null === $operation->getSummary() && '' !== $docBlock->getSummary()) {
|
||||||
$operation->setDescription((string) $docBlock->getDescription());
|
$operation->setSummary($docBlock->getSummary());
|
||||||
|
}
|
||||||
|
if (null === $operation->getDescription() && '' !== (string) $docBlock->getDescription()) {
|
||||||
|
$operation->setDescription((string) $docBlock->getDescription());
|
||||||
|
}
|
||||||
if ($docBlock->hasTag('deprecated')) {
|
if ($docBlock->hasTag('deprecated')) {
|
||||||
$operation->setDeprecated(true);
|
$operation->setDeprecated(true);
|
||||||
}
|
}
|
||||||
|
@ -23,11 +23,18 @@ class RouteMetadataDescriber implements RouteDescriberInterface
|
|||||||
foreach ($this->getOperations($api, $route) as $operation) {
|
foreach ($this->getOperations($api, $route) as $operation) {
|
||||||
$operation->merge(['schemes' => $route->getSchemes()]);
|
$operation->merge(['schemes' => $route->getSchemes()]);
|
||||||
|
|
||||||
foreach ($route->getRequirements() as $parameterName => $requirement) {
|
$requirements = $route->getRequirements();
|
||||||
$parameter = $operation->getParameters()->get($parameterName, 'path');
|
$compiledRoute = $route->compile();
|
||||||
|
|
||||||
|
// Don't include path variables
|
||||||
|
foreach ($compiledRoute->getPathVariables() as $pathVariable) {
|
||||||
|
$parameter = $operation->getParameters()->get($pathVariable, 'path');
|
||||||
$parameter->setRequired(true);
|
$parameter->setRequired(true);
|
||||||
$parameter->setType('string');
|
$parameter->setType('string');
|
||||||
$parameter->setFormat($requirement);
|
|
||||||
|
if (isset($requirements[$pathVariable])) {
|
||||||
|
$parameter->setFormat($requirements[$pathVariable]);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -38,7 +38,7 @@ class FunctionalTest extends WebTestCase
|
|||||||
$operation = $this->getOperation('/nelmio/{foo}', '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->assertNull($operation->getDeprecated());
|
||||||
|
|
||||||
$foo = $operation->getParameters()->get('foo', 'path');
|
$foo = $operation->getParameters()->get('foo', 'path');
|
||||||
$this->assertTrue($foo->getRequired());
|
$this->assertTrue($foo->getRequired());
|
||||||
|
@ -11,7 +11,7 @@
|
|||||||
"require": {
|
"require": {
|
||||||
"php": "^7.0",
|
"php": "^7.0",
|
||||||
"symfony/framework-bundle": "^3.2",
|
"symfony/framework-bundle": "^3.2",
|
||||||
"exsyst/swagger": "dev-master"
|
"exsyst/swagger": "~0.2.1"
|
||||||
},
|
},
|
||||||
"require-dev": {
|
"require-dev": {
|
||||||
"symfony/validator": "^3.2",
|
"symfony/validator": "^3.2",
|
||||||
@ -20,17 +20,20 @@
|
|||||||
"symfony/cache": "^3.2",
|
"symfony/cache": "^3.2",
|
||||||
"symfony/phpunit-bridge": "^3.2",
|
"symfony/phpunit-bridge": "^3.2",
|
||||||
"sensio/framework-extra-bundle": "^3.0",
|
"sensio/framework-extra-bundle": "^3.0",
|
||||||
|
"phpunit/phpunit": "^5.4",
|
||||||
|
|
||||||
"nelmio/api-doc-bundle": "^2.0",
|
"nelmio/api-doc-bundle": "^2.0",
|
||||||
"phpdocumentor/reflection-docblock": "^3.1",
|
"phpdocumentor/reflection-docblock": "^3.1",
|
||||||
"phpunit/phpunit": "^5.4",
|
|
||||||
"zircote/swagger-php": "^2.0",
|
"zircote/swagger-php": "^2.0",
|
||||||
"api-platform/core": "dev-master"
|
"api-platform/core": "dev-master",
|
||||||
|
"friendsofsymfony/rest-bundle": "^2.0"
|
||||||
},
|
},
|
||||||
"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.",
|
"phpdocumentor/reflection-docblock": "For parsing php docs.",
|
||||||
"zircote/swagger-php": "For using swagger annotations.",
|
"zircote/swagger-php": "For using swagger annotations.",
|
||||||
"api-platform/core": "For using an API oriented framework."
|
"api-platform/core": "For using an API oriented framework.",
|
||||||
|
"friendsofsymfony/rest-bundle": "For using the parameters annotations."
|
||||||
},
|
},
|
||||||
"autoload": {
|
"autoload": {
|
||||||
"psr-4": {
|
"psr-4": {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user