mirror of
https://github.com/retailcrm/NelmioApiDocBundle.git
synced 2025-02-02 23:59:26 +03:00
Initialize
This commit is contained in:
parent
92dbf2f617
commit
827d5ea152
3
.gitignore
vendored
Normal file
3
.gitignore
vendored
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
/vendor/*
|
||||||
|
composer.lock
|
||||||
|
.php_cs.cache
|
27
.php_cs
Normal file
27
.php_cs
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
use Symfony\CS\Config\Config;
|
||||||
|
use Symfony\CS\Finder\DefaultFinder;
|
||||||
|
use Symfony\CS\Fixer\Contrib\HeaderCommentFixer;
|
||||||
|
use Symfony\CS\FixerInterface;
|
||||||
|
|
||||||
|
$finder = DefaultFinder::create()
|
||||||
|
->in(__DIR__)
|
||||||
|
;
|
||||||
|
|
||||||
|
$header = <<<EOF
|
||||||
|
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.
|
||||||
|
EOF;
|
||||||
|
HeaderCommentFixer::setHeader($header);
|
||||||
|
|
||||||
|
return Config::create()
|
||||||
|
->level(FixerInterface::SYMFONY_LEVEL)
|
||||||
|
->fixers(array('align_double_arrow', 'header_comment'))
|
||||||
|
->finder($finder)
|
||||||
|
->setUsingCache(true)
|
||||||
|
;
|
29
.travis.yml
Normal file
29
.travis.yml
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
language: php
|
||||||
|
|
||||||
|
php:
|
||||||
|
- 5.5
|
||||||
|
- 5.6
|
||||||
|
- 7.0
|
||||||
|
- hhvm
|
||||||
|
|
||||||
|
sudo: false
|
||||||
|
|
||||||
|
cache:
|
||||||
|
directories:
|
||||||
|
- $HOME/.composer/cache
|
||||||
|
|
||||||
|
branches:
|
||||||
|
only:
|
||||||
|
- master
|
||||||
|
- /^\d+\.\d+$/
|
||||||
|
|
||||||
|
matrix:
|
||||||
|
fast_finish: true
|
||||||
|
include:
|
||||||
|
- php: 5.5
|
||||||
|
env: COMPOSER_FLAGS="--prefer-lowest"
|
||||||
|
|
||||||
|
before_install:
|
||||||
|
- composer self-update
|
||||||
|
|
||||||
|
install: composer update $COMPOSER_FLAGS --prefer-dist
|
41
ApiDocGenerator.php
Normal file
41
ApiDocGenerator.php
Normal file
@ -0,0 +1,41 @@
|
|||||||
|
<?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;
|
||||||
|
|
||||||
|
use EXSyst\Bundle\ApiDocBundle\Extractor\ExtractorInterface;
|
||||||
|
use gossi\swagger\Swagger;
|
||||||
|
|
||||||
|
class ApiDocGenerator
|
||||||
|
{
|
||||||
|
private $extractors;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param ExtractorInterface[] $extractors
|
||||||
|
*/
|
||||||
|
public function __construct(array $extractors)
|
||||||
|
{
|
||||||
|
$this->extractors = $extractors;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return Swagger
|
||||||
|
*/
|
||||||
|
public function extract()
|
||||||
|
{
|
||||||
|
$swagger = new Swagger();
|
||||||
|
foreach ($this->extractors as $extractor) {
|
||||||
|
$extractor->extractIn($swagger);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $swagger;
|
||||||
|
}
|
||||||
|
}
|
19
Extractor/ExtractorInterface.php
Normal file
19
Extractor/ExtractorInterface.php
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
<?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;
|
||||||
|
|
||||||
|
use gossi\swagger\Swagger;
|
||||||
|
|
||||||
|
interface ExtractorInterface
|
||||||
|
{
|
||||||
|
public function extractIn(Swagger $api);
|
||||||
|
}
|
113
Extractor/RouteExtractor.php
Normal file
113
Extractor/RouteExtractor.php
Normal file
@ -0,0 +1,113 @@
|
|||||||
|
<?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;
|
||||||
|
|
||||||
|
use Doctrine\Common\Util\ClassUtils;
|
||||||
|
use EXSyst\Bundle\ApiDocBundle\Extractor\Routing\RouteExtractorInterface;
|
||||||
|
use gossi\swagger\Swagger;
|
||||||
|
use Symfony\Bundle\FrameworkBundle\Controller\ControllerNameParser;
|
||||||
|
use Symfony\Component\Routing\Route;
|
||||||
|
use Symfony\Component\Routing\RouterInterface;
|
||||||
|
|
||||||
|
class RouteExtractor implements ExtractorInterface
|
||||||
|
{
|
||||||
|
private $routeExtractors;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param RouterInterface $router
|
||||||
|
* @param ControllerNameParser $controllerNameParser
|
||||||
|
* @param RouteExtractorInterface[] $extractors
|
||||||
|
*/
|
||||||
|
public function __construct(RouterInterface $router, ControllerNameParser $controllerNameParser, array $routeExtractors)
|
||||||
|
{
|
||||||
|
$this->router = $router;
|
||||||
|
$this->controllerNameParser = $controllerNameParser;
|
||||||
|
$this->routeExtractors = $routeExtractors;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return Swagger
|
||||||
|
*/
|
||||||
|
public function extract()
|
||||||
|
{
|
||||||
|
if (0 === count($this->routeExtractors)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$swagger = new Swagger();
|
||||||
|
foreach ($this->getRoutes() as $route) {
|
||||||
|
// if able to resolve the controller
|
||||||
|
if ($method = $this->getReflectionMethod($route->getDefault('_controller'))) {
|
||||||
|
// Extract as many informations as possible about this route
|
||||||
|
foreach ($this->routeExtractors as $extractor) {
|
||||||
|
$extractor->extractIn($swagger, $route, $method);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return $swagger;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return a list of route to inspect.
|
||||||
|
*
|
||||||
|
* @return Route[] An array of routes
|
||||||
|
*/
|
||||||
|
private function getRoutes()
|
||||||
|
{
|
||||||
|
return $this->router->getRouteCollection()->all();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the ReflectionMethod for the given controller string.
|
||||||
|
*
|
||||||
|
* @param string $controller
|
||||||
|
*
|
||||||
|
* @return \ReflectionMethod|null
|
||||||
|
*/
|
||||||
|
private function getReflectionMethod($controller)
|
||||||
|
{
|
||||||
|
if (false === strpos($controller, '::') && 2 === substr_count($controller, ':')) {
|
||||||
|
$controller = $this->controllerNameParser->parse($controller);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (preg_match('#(.+)::([\w]+)#', $controller, $matches)) {
|
||||||
|
$class = $matches[1];
|
||||||
|
$method = $matches[2];
|
||||||
|
} elseif (class_exists($controller)) {
|
||||||
|
$class = $controller;
|
||||||
|
$method = '__invoke';
|
||||||
|
} else {
|
||||||
|
if (preg_match('#(.+):([\w]+)#', $controller, $matches)) {
|
||||||
|
$controller = $matches[1];
|
||||||
|
$method = $matches[2];
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($this->container->has($controller)) {
|
||||||
|
if (class_exists(ClassUtils::class)) {
|
||||||
|
$class = ClassUtils::getRealClass(get_class($this->container->get($controller)));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!isset($method) && method_exists($class, '__invoke')) {
|
||||||
|
$method = '__invoke';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isset($class) && isset($method)) {
|
||||||
|
try {
|
||||||
|
return new \ReflectionMethod($class, $method);
|
||||||
|
} catch (\ReflectionException $e) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
125
Extractor/Routing/NelmioAnnotationExtractor.php
Normal file
125
Extractor/Routing/NelmioAnnotationExtractor.php
Normal file
@ -0,0 +1,125 @@
|
|||||||
|
<?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 Doctrine\Common\Annotations\Reader;
|
||||||
|
use gossi\swagger\Parameter;
|
||||||
|
use gossi\swagger\Swagger;
|
||||||
|
use Nelmio\ApiDocBundle\Annotation\ApiDoc;
|
||||||
|
use Symfony\Component\Routing\Route;
|
||||||
|
|
||||||
|
class NelmioAnnotationExtractor implements RouteExtractorInterface
|
||||||
|
{
|
||||||
|
use RouteExtractorTrait;
|
||||||
|
|
||||||
|
private $annotationReader;
|
||||||
|
private $nelmioLoaded;
|
||||||
|
|
||||||
|
public function __construct(Reader $annotationReader)
|
||||||
|
{
|
||||||
|
$this->annotationReader = $annotationReader;
|
||||||
|
$this->nelmioLoaded = class_exists(ApiDoc::class);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function extractIn(Swagger $api, Route $route, \ReflectionMethod $reflectionMethod)
|
||||||
|
{
|
||||||
|
if (!$this->nelmioLoaded) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$annotation = $this->annotationReader->getMethodAnnotation($reflectionMethod, ApiDoc::class);
|
||||||
|
// some fields aren't available otherwise
|
||||||
|
$annotationArray = $annotation->toArray();
|
||||||
|
if (null === $annotation) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach ($this->getOperations($api, $route) as $operation) {
|
||||||
|
if ($annotation->getDescription()) {
|
||||||
|
$operation->setDescription($annotation->getDescription());
|
||||||
|
}
|
||||||
|
if (null !== $annotation->getDeprecated()) {
|
||||||
|
$operation->setDeprecated($operation->getDeprecated || $annotation->getDeprecated());
|
||||||
|
}
|
||||||
|
|
||||||
|
// Request parameters
|
||||||
|
foreach ($annotation->getParameters() as $name => $configuration) {
|
||||||
|
$parameter = $operation->getParameters()->get($name, 'formData');
|
||||||
|
if (isset($configuration['required'])) {
|
||||||
|
$parameter->setRequired($parameter->getRequired() || $configuration['required']);
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->configureParameter($parameter, $configuration);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Query parameters
|
||||||
|
foreach ($annotation->getRequirements() as $name => $configuration) {
|
||||||
|
$parameter = $operation->getParameters()->get($name, 'query');
|
||||||
|
$parameter->setRequired(true);
|
||||||
|
|
||||||
|
$this->configureParameter($parameter, $configuration);
|
||||||
|
}
|
||||||
|
foreach ($annotation->getFilters() as $name => $configuration) {
|
||||||
|
$parameter = $operation->getParameters()->get($name, 'query');
|
||||||
|
$this->configureParameter($parameter, $configuration);
|
||||||
|
}
|
||||||
|
|
||||||
|
// External docs
|
||||||
|
if (isset($annotationArray['link'])) {
|
||||||
|
$operation->getExternalDocs()->setUrl($annotationArray['link']);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Responses
|
||||||
|
if (isset($annotationArray['statusCodes'])) {
|
||||||
|
$responses = $operation->getResponses();
|
||||||
|
foreach ($annotationArray['statusCodes'] as $statusCode => $description) {
|
||||||
|
$response = $responses->get($statusCode);
|
||||||
|
$response->setDescription($description);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private function configureParameter(Parameter $parameter, array $configuration)
|
||||||
|
{
|
||||||
|
$dataType = null;
|
||||||
|
if (isset($configuration['dataType'])) {
|
||||||
|
$dataType = $configuration['dataType'];
|
||||||
|
} elseif ($configuration['requirement']) {
|
||||||
|
$dataType = $configuration['requirement'];
|
||||||
|
}
|
||||||
|
|
||||||
|
if ('[]' === substr($requirement, -2)) {
|
||||||
|
$parameter->setType('array');
|
||||||
|
$items = $parameter;
|
||||||
|
do {
|
||||||
|
$items->setCollectionFormat('multi');
|
||||||
|
$requirement = substr($requirement, 0, -2);
|
||||||
|
|
||||||
|
$items = $items->getItems();
|
||||||
|
} while ('[]' === substr($requirement, -2));
|
||||||
|
|
||||||
|
$items->setType(Swagger::T_STRING);
|
||||||
|
$items->setFormat($requirement);
|
||||||
|
} else {
|
||||||
|
$parameter->setType(Swagger::T_STRING);
|
||||||
|
$parameter->setFormat($requirement);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isset($configuration['description'])) {
|
||||||
|
$parameter->setDescription($configuration['description']);
|
||||||
|
}
|
||||||
|
if (isset($configuration['default'])) {
|
||||||
|
$parameter->setDefault($configuration['default']);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
20
Extractor/Routing/RouteExtractorInterface.php
Normal file
20
Extractor/Routing/RouteExtractorInterface.php
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
<?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 gossi\swagger\Swagger;
|
||||||
|
use Symfony\Component\Routing\Route;
|
||||||
|
|
||||||
|
interface RouteExtractorInterface
|
||||||
|
{
|
||||||
|
public function extractIn(Swagger $api, Route $route, \ReflectionMethod $reflectionMethod);
|
||||||
|
}
|
43
Extractor/Routing/RouteExtractorTrait.php
Normal file
43
Extractor/Routing/RouteExtractorTrait.php
Normal file
@ -0,0 +1,43 @@
|
|||||||
|
<?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 gossi\swagger\Operation;
|
||||||
|
use gossi\swagger\Swagger;
|
||||||
|
use Symfony\Component\Routing\Route;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @internal
|
||||||
|
*/
|
||||||
|
trait RouteExtractorTrait
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @internal
|
||||||
|
*
|
||||||
|
* @return Operation[]
|
||||||
|
*/
|
||||||
|
private function getOperations(Swagger $api, Route $route)
|
||||||
|
{
|
||||||
|
$path = $swagger->getPaths()->get($route->getPath());
|
||||||
|
$methods = $route->getMethods() ?: Swagger::$METHODS;
|
||||||
|
foreach ($methods as $method) {
|
||||||
|
$method = strtolower($method);
|
||||||
|
if (!in_array($method, Swagger::$METHODS)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
$operations[] = $path->getOperation($method);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $operations;
|
||||||
|
}
|
||||||
|
}
|
34
Extractor/Routing/RouteMetadataExtractor.php
Normal file
34
Extractor/Routing/RouteMetadataExtractor.php
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
<?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 gossi\swagger\Swagger;
|
||||||
|
use Symfony\Component\Routing\Route;
|
||||||
|
|
||||||
|
class RouteMetadataExtractor implements RouteExtractorInterface
|
||||||
|
{
|
||||||
|
use RouteExtractorTrait;
|
||||||
|
|
||||||
|
public function extractIn(Swagger $api, Route $route, \ReflectionMethod $reflectionMethod)
|
||||||
|
{
|
||||||
|
foreach ($this->getOperations($api, $route) as $operation) {
|
||||||
|
$operation->getSchemes()->addAll($route->getSchemes());
|
||||||
|
|
||||||
|
foreach ($route->getRequirements() as $parameterName => $requirement) {
|
||||||
|
$parameter = $operation->getParameters()->get($parameterName, 'path');
|
||||||
|
$parameter->setRequired(true);
|
||||||
|
$parameter->setType(swagger\Swagger::T_STRING);
|
||||||
|
$parameter->setFormat($requirement);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
33
composer.json
Normal file
33
composer.json
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
{
|
||||||
|
"name": "exsyst/api-doc-bundle",
|
||||||
|
"type": "symfony-bundle",
|
||||||
|
"description": "[WIP] Generates Swagger docs from several sources",
|
||||||
|
"license": "MIT",
|
||||||
|
"authors": [
|
||||||
|
{
|
||||||
|
"name": "EXSyst"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"require": {
|
||||||
|
"php": ">=5.5",
|
||||||
|
"symfony/framework-bundle": "^2.7|^3.0",
|
||||||
|
"gossi/swagger": "^0.2"
|
||||||
|
},
|
||||||
|
"require-dev": {
|
||||||
|
"nelmio/api-doc-bundle": "^2.0",
|
||||||
|
"symfony/phpunit-bridge": "^2.7|^3.0"
|
||||||
|
},
|
||||||
|
"suggest": {
|
||||||
|
"nelmio/api-doc-bundle": "For using the ApiDoc annotation."
|
||||||
|
},
|
||||||
|
"autoload": {
|
||||||
|
"psr-4": {
|
||||||
|
"EXSyst\\Bundle\\ApiDocBundle\\": ""
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"extra": {
|
||||||
|
"branch-alias": {
|
||||||
|
"dev-master": "0.1.x-dev"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
26
phpunit.xml.dist
Normal file
26
phpunit.xml.dist
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<!-- http://phpunit.de/manual/4.1/en/appendixes.configuration.html -->
|
||||||
|
<phpunit xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||||
|
xsi:noNamespaceSchemaLocation="http://schema.phpunit.de/4.1/phpunit.xsd"
|
||||||
|
backupGlobals="false"
|
||||||
|
colors="true"
|
||||||
|
bootstrap="vendor/autoload.php"
|
||||||
|
>
|
||||||
|
<php>
|
||||||
|
<ini name="error_reporting" value="-1" />
|
||||||
|
</php>
|
||||||
|
<testsuites>
|
||||||
|
<testsuite name="EXSyst Api Doc Bundle Test Suite">
|
||||||
|
<directory>./Tests/</directory>
|
||||||
|
</testsuite>
|
||||||
|
</testsuites>
|
||||||
|
<filter>
|
||||||
|
<whitelist>
|
||||||
|
<directory>./</directory>
|
||||||
|
<exclude>
|
||||||
|
<directory>./vendor</directory>
|
||||||
|
<directory>./Tests/</directory>
|
||||||
|
</exclude>
|
||||||
|
</whitelist>
|
||||||
|
</filter>
|
||||||
|
</phpunit>
|
Loading…
x
Reference in New Issue
Block a user