Initialize

This commit is contained in:
Ener-Getick 2016-06-30 23:30:37 +02:00
parent 92dbf2f617
commit 827d5ea152
No known key found for this signature in database
GPG Key ID: 9E5D2DB67BF054DD
12 changed files with 513 additions and 0 deletions

3
.gitignore vendored Normal file
View File

@ -0,0 +1,3 @@
/vendor/*
composer.lock
.php_cs.cache

27
.php_cs Normal file
View 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
View 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
View 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;
}
}

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

View 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) {
}
}
}
}

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

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

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

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