mirror of
https://github.com/retailcrm/NelmioApiDocBundle.git
synced 2025-02-02 15:51:48 +03:00
commit
75eb7ca356
@ -135,6 +135,21 @@ class ApiDoc
|
|||||||
*/
|
*/
|
||||||
private $statusCodes = array();
|
private $statusCodes = array();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var string|null
|
||||||
|
*/
|
||||||
|
private $resourceDescription = null;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var array
|
||||||
|
*/
|
||||||
|
private $responseMap = array();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var array
|
||||||
|
*/
|
||||||
|
private $parsedResponseMap = array();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @var array
|
* @var array
|
||||||
*/
|
*/
|
||||||
@ -241,6 +256,17 @@ class ApiDoc
|
|||||||
if (isset($data['https'])) {
|
if (isset($data['https'])) {
|
||||||
$this->https = $data['https'];
|
$this->https = $data['https'];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (isset($data['resourceDescription'])) {
|
||||||
|
$this->resourceDescription = $data['resourceDescription'];
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isset($data['responseMap'])) {
|
||||||
|
$this->responseMap = $data['responseMap'];
|
||||||
|
if (isset($this->responseMap[200])) {
|
||||||
|
$this->output = $this->responseMap[200];
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -606,6 +632,10 @@ class ApiDoc
|
|||||||
$data['tags'] = $tags;
|
$data['tags'] = $tags;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if ($resourceDescription = $this->resourceDescription) {
|
||||||
|
$data['resourceDescription'] = $resourceDescription;
|
||||||
|
}
|
||||||
|
|
||||||
$data['https'] = $this->https;
|
$data['https'] = $this->https;
|
||||||
$data['authentication'] = $this->authentication;
|
$data['authentication'] = $this->authentication;
|
||||||
$data['authenticationRoles'] = $this->authenticationRoles;
|
$data['authenticationRoles'] = $this->authenticationRoles;
|
||||||
@ -613,4 +643,45 @@ class ApiDoc
|
|||||||
|
|
||||||
return $data;
|
return $data;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return null|string
|
||||||
|
*/
|
||||||
|
public function getResourceDescription()
|
||||||
|
{
|
||||||
|
return $this->resourceDescription;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return array
|
||||||
|
*/
|
||||||
|
public function getResponseMap()
|
||||||
|
{
|
||||||
|
if (!isset($this->responseMap[200]) && null !== $this->output) {
|
||||||
|
$this->responseMap[200] = $this->output;
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this->responseMap;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return array
|
||||||
|
*/
|
||||||
|
public function getParsedResponseMap()
|
||||||
|
{
|
||||||
|
return $this->parsedResponseMap;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param $model
|
||||||
|
* @param $type
|
||||||
|
* @param int $statusCode
|
||||||
|
*/
|
||||||
|
public function setResponseForStatusCode($model, $type, $statusCode = 200)
|
||||||
|
{
|
||||||
|
$this->parsedResponseMap[$statusCode] = array('type' => $type, 'model' => $model);
|
||||||
|
if ($statusCode == 200 && $this->response !== $model) {
|
||||||
|
$this->response = $model;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
132
Command/SwaggerDumpCommand.php
Normal file
132
Command/SwaggerDumpCommand.php
Normal file
@ -0,0 +1,132 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This file is part of the NelmioApiDocBundle.
|
||||||
|
*
|
||||||
|
* (c) Nelmio <hello@nelm.io>
|
||||||
|
*
|
||||||
|
* For the full copyright and license information, please view the LICENSE
|
||||||
|
* file that was distributed with this source code.
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace Nelmio\ApiDocBundle\Command;
|
||||||
|
|
||||||
|
use Symfony\Bundle\FrameworkBundle\Command\ContainerAwareCommand;
|
||||||
|
use Symfony\Component\Console\Input\InputInterface;
|
||||||
|
use Symfony\Component\Console\Input\InputOption;
|
||||||
|
use Symfony\Component\Console\Output\OutputInterface;
|
||||||
|
use Symfony\Component\Filesystem\Exception\IOException;
|
||||||
|
use Symfony\Component\Filesystem\Filesystem;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Symfony2 command to dump Swagger-compliant JSON files.
|
||||||
|
*
|
||||||
|
* @author Bez Hermoso <bez@activelamp.com>
|
||||||
|
*/
|
||||||
|
class SwaggerDumpCommand extends ContainerAwareCommand
|
||||||
|
{
|
||||||
|
protected function configure()
|
||||||
|
{
|
||||||
|
$this
|
||||||
|
->setDescription('Dump Swagger-compliant JSON files.')
|
||||||
|
->addOption('resource', '', InputOption::VALUE_OPTIONAL, 'A specific resource API declaration to dump.')
|
||||||
|
->addOption('all', '', InputOption::VALUE_NONE, 'Dump resource list and all API declarations.')
|
||||||
|
->addOption('list-only', '', InputOption::VALUE_NONE, 'Dump resource list only.')
|
||||||
|
->addArgument('destination', InputOption::VALUE_REQUIRED, 'Directory to dump JSON files in.', null)
|
||||||
|
->setName('api:swagger:dump');
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function execute(InputInterface $input, OutputInterface $output)
|
||||||
|
{
|
||||||
|
$container = $this->getContainer();
|
||||||
|
$destination = $input->getArgument('destination');
|
||||||
|
|
||||||
|
$rootDir = $container->get('kernel')->getRootDir();
|
||||||
|
$rootDir = $rootDir . '/..';
|
||||||
|
|
||||||
|
if (null === $destination) {
|
||||||
|
$destination = realpath($rootDir . '/' . $destination);
|
||||||
|
}
|
||||||
|
|
||||||
|
$fs = new Filesystem();
|
||||||
|
|
||||||
|
if (!$fs->exists($destination)) {
|
||||||
|
$fs->mkdir($destination);
|
||||||
|
}
|
||||||
|
|
||||||
|
$destination = realpath($destination);
|
||||||
|
|
||||||
|
if ($input->getOption('all') && $input->getOption('resource')) {
|
||||||
|
throw new \RuntimeException('Cannot selectively dump a resource with the --all flag.');
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($input->getOption('list-only') && $input->getOption('resource')) {
|
||||||
|
throw new \RuntimeException('Cannot selectively dump a resource with the --list-only flag.');
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($input->getOption('all') && $input->getOption('list-only')) {
|
||||||
|
throw new \RuntimeException('Cannot selectively dump resource list with the --all flag.');
|
||||||
|
}
|
||||||
|
|
||||||
|
$output->writeln('');
|
||||||
|
$output->writeln('Reading annotations...');
|
||||||
|
$extractor = $container->get('nelmio_api_doc.extractor.api_doc_extractor');
|
||||||
|
$data = $extractor->all();
|
||||||
|
|
||||||
|
if ($input->getOption('list-only')) {
|
||||||
|
$this->dumpResourceList($destination, $data, $output);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (false != ($resource = $input->getOption('resource'))) {
|
||||||
|
$this->dumpApiDeclaration($destination, $data, $resource, $output);
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($input->getOption('all')) {
|
||||||
|
$formatter = $container->get('nelmio_api_doc.formatter.swagger_formatter');
|
||||||
|
$this->dumpResourceList($destination, $data, $output);
|
||||||
|
$list = $formatter->format($data);
|
||||||
|
foreach ($list['apis'] as $api) {
|
||||||
|
$this->dumpApiDeclaration($destination, $data, substr($api['path'], 1), $output);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function dumpResourceList($destination, array $data, OutputInterface $output)
|
||||||
|
{
|
||||||
|
$container = $this->getContainer();
|
||||||
|
$formatter = $container->get('nelmio_api_doc.formatter.swagger_formatter');
|
||||||
|
|
||||||
|
$list = $formatter->format($data);
|
||||||
|
|
||||||
|
$fs = new Filesystem();
|
||||||
|
$path = $destination . '/api-docs.json';
|
||||||
|
|
||||||
|
$string = sprintf('<comment>Dump resource list to %s: </comment>', $path);
|
||||||
|
try {
|
||||||
|
$fs->dumpFile($path, json_encode($list));
|
||||||
|
} catch (IOException $e) {
|
||||||
|
$output->writeln($string . ' <error>NOT OK</error>');
|
||||||
|
}
|
||||||
|
$output->writeln($string . '<info>OK</info>');
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function dumpApiDeclaration($destination, array $data, $resource, OutputInterface $output)
|
||||||
|
{
|
||||||
|
$container = $this->getContainer();
|
||||||
|
$formatter = $container->get('nelmio_api_doc.formatter.swagger_formatter');
|
||||||
|
|
||||||
|
$list = $formatter->format($data, '/' . $resource);
|
||||||
|
|
||||||
|
$fs = new Filesystem();
|
||||||
|
$path = sprintf($destination . '/%s.json', $resource);
|
||||||
|
|
||||||
|
$string = sprintf('<comment>Dump API declaration to %s: </comment>', $path);
|
||||||
|
try {
|
||||||
|
$fs->dumpFile($path, json_encode($list));
|
||||||
|
} catch (IOException $e) {
|
||||||
|
$output->writeln($string . ' <error>NOT OK</error>');
|
||||||
|
}
|
||||||
|
$output->writeln($string . '<info>OK</info>');
|
||||||
|
}
|
||||||
|
}
|
@ -11,7 +11,11 @@
|
|||||||
|
|
||||||
namespace Nelmio\ApiDocBundle\Controller;
|
namespace Nelmio\ApiDocBundle\Controller;
|
||||||
|
|
||||||
|
use Nelmio\ApiDocBundle\Formatter\RequestAwareSwaggerFormatter;
|
||||||
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
|
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
|
||||||
|
use Symfony\Component\Filesystem\Filesystem;
|
||||||
|
use Symfony\Component\HttpFoundation\JsonResponse;
|
||||||
|
use Symfony\Component\HttpFoundation\Request;
|
||||||
use Symfony\Component\HttpFoundation\Response;
|
use Symfony\Component\HttpFoundation\Response;
|
||||||
|
|
||||||
class ApiDocController extends Controller
|
class ApiDocController extends Controller
|
||||||
@ -23,4 +27,19 @@ class ApiDocController extends Controller
|
|||||||
|
|
||||||
return new Response($htmlContent, 200, array('Content-Type' => 'text/html'));
|
return new Response($htmlContent, 200, array('Content-Type' => 'text/html'));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function swaggerAction(Request $request, $resource = null)
|
||||||
|
{
|
||||||
|
|
||||||
|
$docs = $this->get('nelmio_api_doc.extractor.api_doc_extractor')->all();
|
||||||
|
$formatter = new RequestAwareSwaggerFormatter($request, $this->get('nelmio_api_doc.formatter.swagger_formatter'));
|
||||||
|
|
||||||
|
$spec = $formatter->format($docs, $resource ? '/' . $resource : null);
|
||||||
|
|
||||||
|
if ($resource !== null && count($spec['apis']) === 0) {
|
||||||
|
throw $this->createNotFoundException(sprintf('Cannot find resource "%s"', $resource));
|
||||||
|
}
|
||||||
|
|
||||||
|
return new JsonResponse($spec);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -43,7 +43,7 @@ class DataTypes
|
|||||||
/**
|
/**
|
||||||
* Returns true if the supplied `actualType` value is considered a primitive type. Returns false, otherwise.
|
* Returns true if the supplied `actualType` value is considered a primitive type. Returns false, otherwise.
|
||||||
*
|
*
|
||||||
* @param string $type
|
* @param string $type
|
||||||
* @return bool
|
* @return bool
|
||||||
*/
|
*/
|
||||||
public static function isPrimitive($type)
|
public static function isPrimitive($type)
|
||||||
|
@ -133,6 +133,25 @@ class Configuration implements ConfigurationInterface
|
|||||||
->end()
|
->end()
|
||||||
->end()
|
->end()
|
||||||
->end()
|
->end()
|
||||||
|
->arrayNode('swagger')
|
||||||
|
->addDefaultsIfNotSet()
|
||||||
|
->children()
|
||||||
|
->scalarNode('api_base_path')->defaultValue('/api')->end()
|
||||||
|
->scalarNode('swagger_version')->defaultValue('1.2')->end()
|
||||||
|
->scalarNode('api_version')->defaultValue('0.1')->end()
|
||||||
|
->arrayNode('info')
|
||||||
|
->addDefaultsIfNotSet()
|
||||||
|
->children()
|
||||||
|
->scalarNode('title')->defaultValue('Symfony2')->end()
|
||||||
|
->scalarNode('description')->defaultValue('My awesome Symfony2 app!')->end()
|
||||||
|
->scalarNode('TermsOfServiceUrl')->defaultValue(null)->end()
|
||||||
|
->scalarNode('contact')->defaultValue(null)->end()
|
||||||
|
->scalarNode('license')->defaultValue(null)->end()
|
||||||
|
->scalarNode('licenseUrl')->defaultValue(null)->end()
|
||||||
|
->end()
|
||||||
|
->end()
|
||||||
|
->end()
|
||||||
|
->end()
|
||||||
->end();
|
->end();
|
||||||
|
|
||||||
return $treeBuilder;
|
return $treeBuilder;
|
||||||
|
@ -11,11 +11,11 @@
|
|||||||
|
|
||||||
namespace Nelmio\ApiDocBundle\DependencyInjection;
|
namespace Nelmio\ApiDocBundle\DependencyInjection;
|
||||||
|
|
||||||
|
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;
|
||||||
use Symfony\Component\DependencyInjection\ContainerBuilder;
|
use Symfony\Component\DependencyInjection\ContainerBuilder;
|
||||||
use Symfony\Component\Config\Definition\Processor;
|
use Symfony\Component\Config\Definition\Processor;
|
||||||
use Symfony\Component\Config\FileLocator;
|
|
||||||
|
|
||||||
class NelmioApiDocExtension extends Extension
|
class NelmioApiDocExtension extends Extension
|
||||||
{
|
{
|
||||||
@ -57,6 +57,12 @@ class NelmioApiDocExtension extends Extension
|
|||||||
if (!interface_exists('\Symfony\Component\Validator\MetadataFactoryInterface')) {
|
if (!interface_exists('\Symfony\Component\Validator\MetadataFactoryInterface')) {
|
||||||
$container->setParameter('nelmio_api_doc.parser.validation_parser.class', 'Nelmio\ApiDocBundle\Parser\ValidationParserLegacy');
|
$container->setParameter('nelmio_api_doc.parser.validation_parser.class', 'Nelmio\ApiDocBundle\Parser\ValidationParserLegacy');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$container->setParameter('nelmio_api_doc.swagger.base_path', $config['swagger']['api_base_path']);
|
||||||
|
$container->setParameter('nelmio_api_doc.swagger.swagger_version', $config['swagger']['swagger_version']);
|
||||||
|
$container->setParameter('nelmio_api_doc.swagger.api_version', $config['swagger']['api_version']);
|
||||||
|
$container->setParameter('nelmio_api_doc.swagger.info', $config['swagger']['info']);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
49
DependencyInjection/SwaggerConfigCompilerPass.php
Normal file
49
DependencyInjection/SwaggerConfigCompilerPass.php
Normal file
@ -0,0 +1,49 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This file is part of the Symfony package.
|
||||||
|
*
|
||||||
|
* (c) Fabien Potencier <fabien@symfony.com>
|
||||||
|
*
|
||||||
|
* For the full copyright and license information, please view the LICENSE
|
||||||
|
* file that was distributed with this source code.
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace Nelmio\ApiDocBundle\DependencyInjection;
|
||||||
|
|
||||||
|
use Symfony\Component\DependencyInjection\Alias;
|
||||||
|
use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
|
||||||
|
use Symfony\Component\DependencyInjection\ContainerBuilder;
|
||||||
|
use Symfony\Component\DependencyInjection\Definition;
|
||||||
|
use Symfony\Component\DependencyInjection\Parameter;
|
||||||
|
use Symfony\Component\DependencyInjection\Reference;
|
||||||
|
use Symfony\Component\Filesystem\Exception\IOException;
|
||||||
|
use Symfony\Component\Filesystem\Filesystem;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Compiler pass that configures the SwaggerFormatter instance.
|
||||||
|
*
|
||||||
|
* @author Bez Hermoso <bez@activelamp.com>
|
||||||
|
*/
|
||||||
|
class SwaggerConfigCompilerPass implements CompilerPassInterface
|
||||||
|
{
|
||||||
|
|
||||||
|
/**
|
||||||
|
* You can modify the container here before it is dumped to PHP code.
|
||||||
|
*
|
||||||
|
* @param ContainerBuilder $container
|
||||||
|
*
|
||||||
|
* @api
|
||||||
|
*/
|
||||||
|
public function process(ContainerBuilder $container)
|
||||||
|
{
|
||||||
|
$formatter = $container->getDefinition('nelmio_api_doc.formatter.swagger_formatter');
|
||||||
|
|
||||||
|
$formatter->addMethodCall('setBasePath', array($container->getParameter('nelmio_api_doc.swagger.base_path')));
|
||||||
|
$formatter->addMethodCall('setApiVersion', array($container->getParameter('nelmio_api_doc.swagger.api_version')));
|
||||||
|
$formatter->addMethodCall('setSwaggerVersion', array($container->getParameter('nelmio_api_doc.swagger.swagger_version')));
|
||||||
|
$formatter->addMethodCall('setInfo', array($container->getParameter('nelmio_api_doc.swagger.info')));
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
@ -317,6 +317,45 @@ class ApiDocExtractor
|
|||||||
$response = $this->generateHumanReadableTypes($response);
|
$response = $this->generateHumanReadableTypes($response);
|
||||||
|
|
||||||
$annotation->setResponse($response);
|
$annotation->setResponse($response);
|
||||||
|
$annotation->setResponseForStatusCode($response, $normalizedOutput, 200);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (count($annotation->getResponseMap()) > 0) {
|
||||||
|
|
||||||
|
foreach ($annotation->getResponseMap() as $code => $modelName) {
|
||||||
|
|
||||||
|
if ('200' === (string) $code && isset($modelName['type']) && isset($modelName['model'])) {
|
||||||
|
/*
|
||||||
|
* Model was already parsed as the default `output` for this ApiDoc.
|
||||||
|
*/
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
$normalizedModel = $this->normalizeClassParameter($modelName);
|
||||||
|
|
||||||
|
$parameters = array();
|
||||||
|
$supportedParsers = array();
|
||||||
|
foreach ($this->getParsers($normalizedModel) as $parser) {
|
||||||
|
if ($parser->supports($normalizedModel)) {
|
||||||
|
$supportedParsers[] = $parser;
|
||||||
|
$parameters = $this->mergeParameters($parameters, $parser->parse($normalizedModel));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach ($supportedParsers as $parser) {
|
||||||
|
if ($parser instanceof PostParserInterface) {
|
||||||
|
$mp = $parser->postParse($normalizedModel, $parameters);
|
||||||
|
$parameters = $this->mergeParameters($parameters, $mp);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$parameters = $this->clearClasses($parameters);
|
||||||
|
$parameters = $this->generateHumanReadableTypes($parameters);
|
||||||
|
|
||||||
|
$annotation->setResponseForStatusCode($parameters, $normalizedModel, $code);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return $annotation;
|
return $annotation;
|
||||||
@ -455,16 +494,20 @@ class ApiDocExtractor
|
|||||||
/**
|
/**
|
||||||
* Creates a human-readable version of the `actualType`. `subType` is taken into account.
|
* Creates a human-readable version of the `actualType`. `subType` is taken into account.
|
||||||
*
|
*
|
||||||
* @param string $actualType
|
* @param string $actualType
|
||||||
* @param string $subType
|
* @param string $subType
|
||||||
* @return string
|
* @return string
|
||||||
*/
|
*/
|
||||||
protected function generateHumanReadableType($actualType, $subType)
|
protected function generateHumanReadableType($actualType, $subType)
|
||||||
{
|
{
|
||||||
if ($actualType == DataTypes::MODEL) {
|
if ($actualType == DataTypes::MODEL) {
|
||||||
$parts = explode('\\', $subType);
|
|
||||||
|
|
||||||
return sprintf('object (%s)', end($parts));
|
if (class_exists($subType)) {
|
||||||
|
$parts = explode('\\', $subType);
|
||||||
|
return sprintf('object (%s)', end($parts));
|
||||||
|
}
|
||||||
|
|
||||||
|
return sprintf('object (%s)', $subType);
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($actualType == DataTypes::COLLECTION) {
|
if ($actualType == DataTypes::COLLECTION) {
|
||||||
@ -475,7 +518,6 @@ class ApiDocExtractor
|
|||||||
|
|
||||||
if (class_exists($subType)) {
|
if (class_exists($subType)) {
|
||||||
$parts = explode('\\', $subType);
|
$parts = explode('\\', $subType);
|
||||||
|
|
||||||
return sprintf('array of objects (%s)', end($parts));
|
return sprintf('array of objects (%s)', end($parts));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
74
Formatter/RequestAwareSwaggerFormatter.php
Normal file
74
Formatter/RequestAwareSwaggerFormatter.php
Normal file
@ -0,0 +1,74 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This file is part of the NelmioApiDocBundle.
|
||||||
|
*
|
||||||
|
* (c) Nelmio <hello@nelm.io>
|
||||||
|
*
|
||||||
|
* For the full copyright and license information, please view the LICENSE
|
||||||
|
* file that was distributed with this source code.
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace Nelmio\ApiDocBundle\Formatter;
|
||||||
|
|
||||||
|
|
||||||
|
use Nelmio\ApiDocBundle\Annotation\ApiDoc;
|
||||||
|
use Symfony\Component\HttpFoundation\Request;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Extends SwaggerFormatter which takes into account the request's base URL when generating the documents for direct swagger-ui consumption.
|
||||||
|
*
|
||||||
|
* @author Bezalel Hermoso <bezalelhermoso@gmail.com>
|
||||||
|
*/
|
||||||
|
class RequestAwareSwaggerFormatter implements FormatterInterface
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @var \Symfony\Component\HttpFoundation\Request
|
||||||
|
*/
|
||||||
|
protected $request;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var SwaggerFormatter
|
||||||
|
*/
|
||||||
|
protected $formatter;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param Request $request
|
||||||
|
* @param SwaggerFormatter $formatter
|
||||||
|
*/
|
||||||
|
public function __construct(Request $request, SwaggerFormatter $formatter)
|
||||||
|
{
|
||||||
|
$this->request = $request;
|
||||||
|
$this->formatter = $formatter;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Format a collection of documentation data.
|
||||||
|
*
|
||||||
|
* @param array $collection
|
||||||
|
* @param null $resource
|
||||||
|
* @internal param $array [ApiDoc] $collection
|
||||||
|
* @return string|array
|
||||||
|
*/
|
||||||
|
public function format(array $collection, $resource = null)
|
||||||
|
{
|
||||||
|
$result = $this->formatter->format($collection, $resource);
|
||||||
|
|
||||||
|
if ($resource !== null) {
|
||||||
|
$result['basePath'] = $this->request->getBaseUrl() . $result['basePath'];
|
||||||
|
}
|
||||||
|
|
||||||
|
return $result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Format documentation data for one route.
|
||||||
|
*
|
||||||
|
* @param ApiDoc $annotation
|
||||||
|
* return string|array
|
||||||
|
*/
|
||||||
|
public function formatOne(ApiDoc $annotation)
|
||||||
|
{
|
||||||
|
return $this->formatter->formatOne($annotation);
|
||||||
|
}
|
||||||
|
}
|
609
Formatter/SwaggerFormatter.php
Normal file
609
Formatter/SwaggerFormatter.php
Normal file
@ -0,0 +1,609 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This file is part of the NelmioApiDocBundle.
|
||||||
|
*
|
||||||
|
* (c) Nelmio <hello@nelm.io>
|
||||||
|
*
|
||||||
|
* For the full copyright and license information, please view the LICENSE
|
||||||
|
* file that was distributed with this source code.
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace Nelmio\ApiDocBundle\Formatter;
|
||||||
|
|
||||||
|
|
||||||
|
use Nelmio\ApiDocBundle\Annotation\ApiDoc;
|
||||||
|
use Nelmio\ApiDocBundle\DataTypes;
|
||||||
|
use Symfony\Component\HttpFoundation\Response;
|
||||||
|
use Symfony\Component\Routing\Router;
|
||||||
|
use Symfony\Component\Routing\RouterInterface;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Produces Swagger-compliant resource lists and API declarations as defined here:
|
||||||
|
* https://github.com/wordnik/swagger-spec/blob/master/versions/1.2.md
|
||||||
|
*
|
||||||
|
* This formatter produces an array. Therefore output still needs to be `json_encode`d before passing on as HTTP response.
|
||||||
|
*
|
||||||
|
* @author Bezalel Hermoso <bezalelhermoso@gmail.com>
|
||||||
|
*/
|
||||||
|
class SwaggerFormatter implements FormatterInterface
|
||||||
|
{
|
||||||
|
protected $basePath;
|
||||||
|
|
||||||
|
protected $apiVersion;
|
||||||
|
|
||||||
|
protected $swaggerVersion;
|
||||||
|
|
||||||
|
protected $info = array();
|
||||||
|
|
||||||
|
protected $typeMap = array(
|
||||||
|
DataTypes::INTEGER => 'integer',
|
||||||
|
DataTypes::FLOAT => 'number',
|
||||||
|
DataTypes::STRING => 'string',
|
||||||
|
DataTypes::BOOLEAN => 'boolean',
|
||||||
|
DataTypes::FILE => 'string',
|
||||||
|
DataTypes::DATE => 'string',
|
||||||
|
DataTypes::DATETIME => 'string',
|
||||||
|
);
|
||||||
|
|
||||||
|
protected $formatMap = array(
|
||||||
|
DataTypes::INTEGER => 'int32',
|
||||||
|
DataTypes::FLOAT => 'float',
|
||||||
|
DataTypes::FILE => 'byte',
|
||||||
|
DataTypes::DATE => 'date',
|
||||||
|
DataTypes::DATETIME => 'date-time',
|
||||||
|
);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Format a collection of documentation data.
|
||||||
|
*
|
||||||
|
* If resource is provided, an API declaration for that resource is produced. Otherwise, a resource listing is returned.
|
||||||
|
*
|
||||||
|
* @param array|ApiDoc[] $collection
|
||||||
|
* @param null|string $resource
|
||||||
|
* @return string|array
|
||||||
|
*/
|
||||||
|
public function format(array $collection, $resource = null)
|
||||||
|
{
|
||||||
|
if ($resource === null) {
|
||||||
|
return $this->produceResourceListing($collection);
|
||||||
|
} else {
|
||||||
|
return $this->produceApiDeclaration($collection, $resource);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Formats the collection into Swagger-compliant output.
|
||||||
|
*
|
||||||
|
* @param array $collection
|
||||||
|
* @return array
|
||||||
|
*/
|
||||||
|
public function produceResourceListing(array $collection)
|
||||||
|
{
|
||||||
|
$resourceList = array(
|
||||||
|
'swaggerVersion' => (string) $this->swaggerVersion,
|
||||||
|
'apis' => array(),
|
||||||
|
'apiVersion' => (string) $this->apiVersion,
|
||||||
|
'info' => $this->getInfo(),
|
||||||
|
'authorizations' => $this->getAuthorizations(),
|
||||||
|
);
|
||||||
|
|
||||||
|
$apis = &$resourceList['apis'];
|
||||||
|
|
||||||
|
foreach ($collection as $item) {
|
||||||
|
|
||||||
|
/** @var $apiDoc ApiDoc */
|
||||||
|
$apiDoc = $item['annotation'];
|
||||||
|
$resource = $item['resource'];
|
||||||
|
|
||||||
|
if (!$apiDoc->isResource()) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
$subPath = $this->stripBasePath($resource);
|
||||||
|
$normalizedName = $this->normalizeResourcePath($subPath);
|
||||||
|
|
||||||
|
$apis[] = array(
|
||||||
|
'path' => '/' . $normalizedName,
|
||||||
|
'description' => $apiDoc->getResourceDescription(),
|
||||||
|
);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
return $resourceList;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function getAuthorizations()
|
||||||
|
{
|
||||||
|
return array();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return array
|
||||||
|
*/
|
||||||
|
protected function getInfo()
|
||||||
|
{
|
||||||
|
return $this->info;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Format documentation data for one route.
|
||||||
|
*
|
||||||
|
* @param ApiDoc $annotation
|
||||||
|
* return string|array
|
||||||
|
* @throws \BadMethodCallException
|
||||||
|
*/
|
||||||
|
public function formatOne(ApiDoc $annotation)
|
||||||
|
{
|
||||||
|
throw new \BadMethodCallException(sprintf('%s does not support formatting a single ApiDoc only.', __CLASS__));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Formats collection to produce a Swagger-compliant API declaration for the given resource.
|
||||||
|
*
|
||||||
|
* @param array $collection
|
||||||
|
* @param string $resource
|
||||||
|
* @return array
|
||||||
|
*/
|
||||||
|
protected function produceApiDeclaration(array $collection, $resource)
|
||||||
|
{
|
||||||
|
|
||||||
|
$apiDeclaration = array(
|
||||||
|
'swaggerVersion' => (string) $this->swaggerVersion,
|
||||||
|
'apiVersion' => (string) $this->apiVersion,
|
||||||
|
'basePath' => $this->basePath,
|
||||||
|
'resourcePath' => $resource,
|
||||||
|
'apis' => array(),
|
||||||
|
'models' => array(),
|
||||||
|
'produces' => array(),
|
||||||
|
'consumes' => array(),
|
||||||
|
'authorizations' => array(),
|
||||||
|
);
|
||||||
|
|
||||||
|
$main = null;
|
||||||
|
|
||||||
|
$apiBag = array();
|
||||||
|
|
||||||
|
$models = array();
|
||||||
|
|
||||||
|
|
||||||
|
foreach ($collection as $item) {
|
||||||
|
|
||||||
|
/** @var $apiDoc ApiDoc */
|
||||||
|
$apiDoc = $item['annotation'];
|
||||||
|
$itemResource = $this->stripBasePath($item['resource']);
|
||||||
|
|
||||||
|
$route = $apiDoc->getRoute();
|
||||||
|
|
||||||
|
$itemResource = $this->normalizeResourcePath($itemResource);
|
||||||
|
|
||||||
|
if ('/' . $itemResource !== $resource) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
$compiled = $route->compile();
|
||||||
|
|
||||||
|
$path = $this->stripBasePath($route->getPath());
|
||||||
|
|
||||||
|
if (!isset($apiBag[$path])) {
|
||||||
|
$apiBag[$path] = array();
|
||||||
|
}
|
||||||
|
|
||||||
|
$parameters = array();
|
||||||
|
$responseMessages = array();
|
||||||
|
|
||||||
|
foreach ($compiled->getPathVariables() as $paramValue) {
|
||||||
|
$parameter = array(
|
||||||
|
'paramType' => 'path',
|
||||||
|
'name' => $paramValue,
|
||||||
|
'type' => 'string',
|
||||||
|
'required' => true,
|
||||||
|
);
|
||||||
|
|
||||||
|
if ($paramValue === '_format' && false != ($req = $route->getRequirement('_format'))) {
|
||||||
|
$parameter['enum'] = explode('|', $req);
|
||||||
|
}
|
||||||
|
|
||||||
|
$parameters[] = $parameter;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isset($data['filters'])) {
|
||||||
|
$parameters = array_merge($parameters, $this->deriveQueryParameters($data['filters']));
|
||||||
|
}
|
||||||
|
|
||||||
|
$data = $apiDoc->toArray();
|
||||||
|
|
||||||
|
if (isset($data['parameters'])) {
|
||||||
|
$parameters = array_merge($parameters, $this->deriveParameters($data['parameters'], $models));
|
||||||
|
}
|
||||||
|
|
||||||
|
$responseMap = $apiDoc->getParsedResponseMap();
|
||||||
|
|
||||||
|
$statusMessages = isset($data['statusCodes']) ? $data['statusCodes'] : array();
|
||||||
|
|
||||||
|
foreach ($responseMap as $statusCode => $prop) {
|
||||||
|
|
||||||
|
if (isset($statusMessages[$statusCode])) {
|
||||||
|
$message = is_array($statusMessages[$statusCode]) ? implode('; ', $statusMessages[$statusCode]) : $statusCode[$statusCode];
|
||||||
|
} else {
|
||||||
|
$message = sprintf('See standard HTTP status code reason for %s', $statusCode);
|
||||||
|
}
|
||||||
|
|
||||||
|
$responseModel = array(
|
||||||
|
'code' => $statusCode,
|
||||||
|
'message' => $message,
|
||||||
|
'responseModel' => $this->registerModel($prop['type']['class'], $prop['model'], '', $models),
|
||||||
|
);
|
||||||
|
$responseMessages[$statusCode] = $responseModel;
|
||||||
|
}
|
||||||
|
|
||||||
|
$unmappedMessages = array_diff(array_keys($statusMessages), array_keys($responseMessages));
|
||||||
|
|
||||||
|
foreach ($unmappedMessages as $code) {
|
||||||
|
$responseMessages[$code] = array(
|
||||||
|
'code' => $code,
|
||||||
|
'message' => is_array($statusMessages[$code]) ? implode('; ', $statusMessages[$code]) : $statusMessages[$code],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
$type = isset($responseMessages[200]['responseModel']) ? $responseMessages[200]['responseModel'] : null;
|
||||||
|
|
||||||
|
foreach ($apiDoc->getRoute()->getMethods() as $method) {
|
||||||
|
$operation = array(
|
||||||
|
'method' => $method,
|
||||||
|
'summary' => $apiDoc->getDescription(),
|
||||||
|
'nickname' => $this->generateNickname($method, $itemResource),
|
||||||
|
'parameters' => $parameters,
|
||||||
|
'responseMessages' => array_values($responseMessages),
|
||||||
|
);
|
||||||
|
|
||||||
|
if ($type !== null) {
|
||||||
|
$operation['type'] = $type;
|
||||||
|
}
|
||||||
|
|
||||||
|
$apiBag[$path][] = $operation;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$apiDeclaration['resourcePath'] = $resource;
|
||||||
|
|
||||||
|
foreach ($apiBag as $path => $operations) {
|
||||||
|
$apiDeclaration['apis'][] = array(
|
||||||
|
'path' => $path,
|
||||||
|
'operations' => $operations,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
$apiDeclaration['models'] = $models;
|
||||||
|
|
||||||
|
return $apiDeclaration;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Slugify a URL path. Trims out path parameters wrapped in curly brackets.
|
||||||
|
*
|
||||||
|
* @param $path
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
protected function normalizeResourcePath($path)
|
||||||
|
{
|
||||||
|
$path = preg_replace('/({.*?})/', '', $path);
|
||||||
|
$path = trim(preg_replace('/[^0-9a-zA-Z]/', '-', $path), '-');
|
||||||
|
$path = preg_replace('/-+/', '-', $path);
|
||||||
|
return $path;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param $path
|
||||||
|
*/
|
||||||
|
public function setBasePath($path)
|
||||||
|
{
|
||||||
|
$this->basePath = $path;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Formats query parameters to Swagger-compliant form.
|
||||||
|
*
|
||||||
|
* @param array $input
|
||||||
|
* @return array
|
||||||
|
*/
|
||||||
|
protected function deriveQueryParameters(array $input)
|
||||||
|
{
|
||||||
|
$parameters = array();
|
||||||
|
|
||||||
|
foreach ($input as $name => $prop) {
|
||||||
|
$parameters[] = array(
|
||||||
|
'paramType' => 'query',
|
||||||
|
'name' => $name,
|
||||||
|
'type' => isset($this->typeMap[$prop['dataType']]) ? $this->typeMap[$prop['dataType']] : 'string',
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $parameters;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Builds a Swagger-compliant parameter list from the provided parameter array. Models are built when necessary.
|
||||||
|
*
|
||||||
|
* @param array $input
|
||||||
|
* @param array $models
|
||||||
|
* @return array
|
||||||
|
*/
|
||||||
|
protected function deriveParameters(array $input, array &$models)
|
||||||
|
{
|
||||||
|
|
||||||
|
$parameters = array();
|
||||||
|
|
||||||
|
foreach ($input as $name => $prop) {
|
||||||
|
|
||||||
|
$type = null;
|
||||||
|
$format = null;
|
||||||
|
$ref = null;
|
||||||
|
$enum = null;
|
||||||
|
$items = null;
|
||||||
|
|
||||||
|
if (isset ($this->typeMap[$prop['actualType']])) {
|
||||||
|
$type = $this->typeMap[$prop['actualType']];
|
||||||
|
} else {
|
||||||
|
switch ($prop['actualType']) {
|
||||||
|
case DataTypes::ENUM:
|
||||||
|
$type = 'string';
|
||||||
|
if (isset($prop['format'])) {
|
||||||
|
$enum = array_keys(json_decode($prop['format'], true));
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case DataTypes::MODEL:
|
||||||
|
$ref =
|
||||||
|
$this->registerModel(
|
||||||
|
$prop['subType'],
|
||||||
|
isset($prop['children']) ? $prop['children'] : null,
|
||||||
|
$prop['description'] ?: $prop['dataType'],
|
||||||
|
$models
|
||||||
|
);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isset($this->formatMap[$prop['actualType']])) {
|
||||||
|
$format = $this->formatMap[$prop['actualType']];
|
||||||
|
}
|
||||||
|
|
||||||
|
if (null === $type && null === $ref) {
|
||||||
|
/* `type` or `$ref` is required. Continue to next of none of these was determined. */
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
$parameter = array(
|
||||||
|
'paramType' => 'form',
|
||||||
|
'name' => $name,
|
||||||
|
);
|
||||||
|
|
||||||
|
if (null !== $type) {
|
||||||
|
$parameter['type'] = $type;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (null !== $ref) {
|
||||||
|
$parameter['$ref'] = $ref;
|
||||||
|
$parameter['type'] = $ref;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (null !== $format) {
|
||||||
|
$parameter['format'] = $format;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (is_array($enum) && count($enum) > 0) {
|
||||||
|
$parameter['enum'] = $enum;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($prop['default'] !== null) {
|
||||||
|
$parameter['defaultValue'] = $prop['default'];
|
||||||
|
}
|
||||||
|
|
||||||
|
$parameters[] = $parameter;
|
||||||
|
}
|
||||||
|
|
||||||
|
return $parameters;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Registers a model into the model array. Returns a unique identifier for the model to be used in `$ref` properties.
|
||||||
|
*
|
||||||
|
* @param $className
|
||||||
|
* @param array $parameters
|
||||||
|
* @param string $description
|
||||||
|
* @param $models
|
||||||
|
* @return mixed
|
||||||
|
*/
|
||||||
|
public function registerModel($className, array $parameters = null, $description = '', &$models)
|
||||||
|
{
|
||||||
|
if (isset ($models[$className])) {
|
||||||
|
return $models[$className]['id'];
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Converts \Fully\Qualified\Class\Name to Fully.Qualified.Class.Name
|
||||||
|
*/
|
||||||
|
$id = preg_replace('#(\\\|[^A-Za-z0-9])#', '.', $className);
|
||||||
|
//Replace duplicate dots.
|
||||||
|
$id = preg_replace('/\.+/', '.', $id);
|
||||||
|
//Replace trailing dots.
|
||||||
|
$id = preg_replace('/^\./', '', $id);
|
||||||
|
|
||||||
|
$model = array(
|
||||||
|
'id' => $id,
|
||||||
|
'description' => $description,
|
||||||
|
);
|
||||||
|
|
||||||
|
if (is_array($parameters)) {
|
||||||
|
|
||||||
|
$required = array();
|
||||||
|
$properties = array();
|
||||||
|
|
||||||
|
foreach ($parameters as $name => $prop) {
|
||||||
|
|
||||||
|
$subParam = array();
|
||||||
|
|
||||||
|
if ($prop['actualType'] === DataTypes::MODEL) {
|
||||||
|
|
||||||
|
$subParam['$ref'] = $this->registerModel(
|
||||||
|
$prop['subType'],
|
||||||
|
isset($prop['children']) ? $prop['children'] : null,
|
||||||
|
$prop['description'] ?: $prop['dataType'],
|
||||||
|
$models
|
||||||
|
);
|
||||||
|
|
||||||
|
} else {
|
||||||
|
|
||||||
|
$type = null;
|
||||||
|
$format = null;
|
||||||
|
$items = null;
|
||||||
|
$enum = null;
|
||||||
|
$ref = null;
|
||||||
|
|
||||||
|
if (isset($this->typeMap[$prop['actualType']])) {
|
||||||
|
$type = $this->typeMap[$prop['actualType']];
|
||||||
|
} else{
|
||||||
|
|
||||||
|
switch ($prop['actualType']) {
|
||||||
|
case DataTypes::ENUM:
|
||||||
|
$type = 'string';
|
||||||
|
if (isset($prop['format'])) {
|
||||||
|
$enum = array_keys(json_decode($prop['format'], true));
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case DataTypes::COLLECTION:
|
||||||
|
$type = 'array';
|
||||||
|
|
||||||
|
if ($prop['subType'] === DataTypes::MODEL) {
|
||||||
|
|
||||||
|
} else {
|
||||||
|
|
||||||
|
if ($prop['subType'] === null
|
||||||
|
|| isset($this->typeMap[$prop['subType']])) {
|
||||||
|
$items = array(
|
||||||
|
'type' => 'string',
|
||||||
|
);
|
||||||
|
} elseif (!isset($this->typeMap[$prop['subType']])) {
|
||||||
|
$items = array(
|
||||||
|
'$ref' =>
|
||||||
|
$this->registerModel(
|
||||||
|
$prop['subType'],
|
||||||
|
isset($prop['children']) ? $prop['children'] : null,
|
||||||
|
$prop['description'] ?: $prop['dataType'],
|
||||||
|
$models
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/* @TODO: Handle recursion if subtype is a model. */
|
||||||
|
break;
|
||||||
|
|
||||||
|
case DataTypes::MODEL:
|
||||||
|
$ref = $this->registerModel(
|
||||||
|
$prop['subType'],
|
||||||
|
isset($prop['children']) ? $prop['children'] : null,
|
||||||
|
$prop['description'] ?: $prop['dataType'],
|
||||||
|
$models
|
||||||
|
);
|
||||||
|
$type = $ref;
|
||||||
|
/* @TODO: Handle recursion. */
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isset($this->formatMap[$prop['actualType']])) {
|
||||||
|
$format = $this->formatMap[$prop['actualType']];
|
||||||
|
}
|
||||||
|
|
||||||
|
$subParam = array(
|
||||||
|
'type' => $type,
|
||||||
|
'description' => empty($prop['description']) === false ? (string) $prop['description'] : $prop['dataType'],
|
||||||
|
);
|
||||||
|
|
||||||
|
if ($format !== null) {
|
||||||
|
$subParam['format'] = $format;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($enum !== null) {
|
||||||
|
$subParam['enum'] = $enum;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($ref !== null) {
|
||||||
|
$subParam['$ref'] = $ref;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($items !== null) {
|
||||||
|
$subParam['items'] = $items;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($prop['required']) {
|
||||||
|
$required[] = $name;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
$properties[$name] = $subParam;
|
||||||
|
}
|
||||||
|
|
||||||
|
$model['properties'] = $properties;
|
||||||
|
$model['required'] = $required;
|
||||||
|
$models[$id] = $model;
|
||||||
|
}
|
||||||
|
|
||||||
|
return $id;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param mixed $swaggerVersion
|
||||||
|
*/
|
||||||
|
public function setSwaggerVersion($swaggerVersion)
|
||||||
|
{
|
||||||
|
$this->swaggerVersion = $swaggerVersion;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param mixed $apiVersion
|
||||||
|
*/
|
||||||
|
public function setApiVersion($apiVersion)
|
||||||
|
{
|
||||||
|
$this->apiVersion = $apiVersion;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param mixed $info
|
||||||
|
*/
|
||||||
|
public function setInfo($info)
|
||||||
|
{
|
||||||
|
$this->info = $info;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Strips the base path from a URL path.
|
||||||
|
*
|
||||||
|
* @param $path
|
||||||
|
* @return mixed
|
||||||
|
*/
|
||||||
|
protected function stripBasePath($path)
|
||||||
|
{
|
||||||
|
$pattern = sprintf('#%s#', preg_quote($this->basePath));
|
||||||
|
$subPath = preg_replace($pattern, '', $path);
|
||||||
|
return $subPath;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generate nicknames based on support HTTP methods and the resource name.
|
||||||
|
*
|
||||||
|
* @param $method
|
||||||
|
* @param $resource
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
protected function generateNickname($method, $resource)
|
||||||
|
{
|
||||||
|
$resource = preg_replace('#/^#', '', $resource);
|
||||||
|
$resource = $this->normalizeResourcePath($resource);
|
||||||
|
return sprintf('%s_%s', strtolower($method), $resource);
|
||||||
|
}
|
||||||
|
}
|
@ -2,6 +2,7 @@
|
|||||||
|
|
||||||
namespace Nelmio\ApiDocBundle;
|
namespace Nelmio\ApiDocBundle;
|
||||||
|
|
||||||
|
use Nelmio\ApiDocBundle\DependencyInjection\SwaggerConfigCompilerPass;
|
||||||
use Symfony\Component\HttpKernel\Bundle\Bundle;
|
use Symfony\Component\HttpKernel\Bundle\Bundle;
|
||||||
use Symfony\Component\DependencyInjection\ContainerBuilder;
|
use Symfony\Component\DependencyInjection\ContainerBuilder;
|
||||||
use Nelmio\ApiDocBundle\DependencyInjection\LoadExtractorParsersPass;
|
use Nelmio\ApiDocBundle\DependencyInjection\LoadExtractorParsersPass;
|
||||||
@ -17,5 +18,6 @@ class NelmioApiDocBundle extends Bundle
|
|||||||
$container->addCompilerPass(new LoadExtractorParsersPass());
|
$container->addCompilerPass(new LoadExtractorParsersPass());
|
||||||
$container->addCompilerPass(new RegisterExtractorParsersPass());
|
$container->addCompilerPass(new RegisterExtractorParsersPass());
|
||||||
$container->addCompilerPass(new ExtractorHandlerCompilerPass());
|
$container->addCompilerPass(new ExtractorHandlerCompilerPass());
|
||||||
|
$container->addCompilerPass(new SwaggerConfigCompilerPass());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -8,6 +8,7 @@
|
|||||||
<parameter key="nelmio_api_doc.formatter.markdown_formatter.class">Nelmio\ApiDocBundle\Formatter\MarkdownFormatter</parameter>
|
<parameter key="nelmio_api_doc.formatter.markdown_formatter.class">Nelmio\ApiDocBundle\Formatter\MarkdownFormatter</parameter>
|
||||||
<parameter key="nelmio_api_doc.formatter.simple_formatter.class">Nelmio\ApiDocBundle\Formatter\SimpleFormatter</parameter>
|
<parameter key="nelmio_api_doc.formatter.simple_formatter.class">Nelmio\ApiDocBundle\Formatter\SimpleFormatter</parameter>
|
||||||
<parameter key="nelmio_api_doc.formatter.html_formatter.class">Nelmio\ApiDocBundle\Formatter\HtmlFormatter</parameter>
|
<parameter key="nelmio_api_doc.formatter.html_formatter.class">Nelmio\ApiDocBundle\Formatter\HtmlFormatter</parameter>
|
||||||
|
<parameter key="nelmio_api_doc.formatter.swagger_formatter.class">Nelmio\ApiDocBundle\Formatter\SwaggerFormatter</parameter>
|
||||||
<parameter key="nelmio_api_doc.sandbox.authentication">null</parameter>
|
<parameter key="nelmio_api_doc.sandbox.authentication">null</parameter>
|
||||||
</parameters>
|
</parameters>
|
||||||
|
|
||||||
@ -56,6 +57,8 @@
|
|||||||
<argument>%nelmio_api_doc.sandbox.authentication%</argument>
|
<argument>%nelmio_api_doc.sandbox.authentication%</argument>
|
||||||
</call>
|
</call>
|
||||||
</service>
|
</service>
|
||||||
|
<service id="nelmio_api_doc.formatter.swagger_formatter" class="%nelmio_api_doc.formatter.swagger_formatter.class%"
|
||||||
|
parent="nelmio_api_doc.formatter.abstract_formatter" />
|
||||||
</services>
|
</services>
|
||||||
|
|
||||||
</container>
|
</container>
|
||||||
|
@ -2,4 +2,4 @@ nelmio_api_doc_index:
|
|||||||
pattern: /
|
pattern: /
|
||||||
defaults: { _controller: NelmioApiDocBundle:ApiDoc:index }
|
defaults: { _controller: NelmioApiDocBundle:ApiDoc:index }
|
||||||
requirements:
|
requirements:
|
||||||
_method: GET
|
_method: GET
|
11
Resources/config/swagger_routing.yml
Normal file
11
Resources/config/swagger_routing.yml
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
nelmio_api_doc_swagger_resource_list:
|
||||||
|
pattern: /
|
||||||
|
defaults: { _controller: NelmioApiDocBundle:ApiDoc:swagger }
|
||||||
|
requirements:
|
||||||
|
_method: GET
|
||||||
|
|
||||||
|
nelmio_api_doc_swagger_api_declaration:
|
||||||
|
pattern: /{resource}
|
||||||
|
defaults: { _controller: NelmioApiDocBundle:ApiDoc:swagger }
|
||||||
|
requirements:
|
||||||
|
_method: GET
|
116
Resources/doc/swagger-support.md
Normal file
116
Resources/doc/swagger-support.md
Normal file
@ -0,0 +1,116 @@
|
|||||||
|
NelmioApiDocBundle
|
||||||
|
===================
|
||||||
|
|
||||||
|
##Swagger support##
|
||||||
|
|
||||||
|
It is now possible to make your application produce Swagger-compliant JSON output based on `@ApiDoc` annotations, which can be used for consumption by [swagger-ui](https://github.com/wordnik/swagger-ui).
|
||||||
|
|
||||||
|
###Annotations###
|
||||||
|
|
||||||
|
A couple of properties has been added to `@ApiDoc`:
|
||||||
|
|
||||||
|
To define a __resource description__:
|
||||||
|
|
||||||
|
```php
|
||||||
|
<?php
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @ApiDoc(
|
||||||
|
* resource=true,
|
||||||
|
* resourceDescription="Operations on users.",
|
||||||
|
* description="Retrieve list of users."
|
||||||
|
* )
|
||||||
|
*/
|
||||||
|
public function listUsersAction()
|
||||||
|
{
|
||||||
|
/* Stuff */
|
||||||
|
}
|
||||||
|
|
||||||
|
```
|
||||||
|
|
||||||
|
The `resourceDescription` is distinct from `description` as it applies to the whole resource group and not just the particular API endpoint.
|
||||||
|
|
||||||
|
Swagger provides you the ability to specify alternate output models for different status codes. Example, `200` would return your default resource object in JSON form, but `400` may return a custom validation error list object. This can be specified through the `responseMap` property:
|
||||||
|
|
||||||
|
```php
|
||||||
|
<?php
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @ApiDoc(
|
||||||
|
* description="Retrieve list of users.",
|
||||||
|
* statusCodes={
|
||||||
|
* 400 = "Validation failed."
|
||||||
|
* },
|
||||||
|
* responseMap={
|
||||||
|
* 200 = "FooBundle\Entity\User",
|
||||||
|
* 400 = {
|
||||||
|
* "class"="CommonBundle\Model\ValidationErrors",
|
||||||
|
* "parsers"={"Nelmio\ApiDocBundle\Parser\JmsMetadataParser"}
|
||||||
|
* }
|
||||||
|
* }
|
||||||
|
* )
|
||||||
|
*/
|
||||||
|
public function updateUserAction()
|
||||||
|
{
|
||||||
|
/* Stuff */
|
||||||
|
}
|
||||||
|
|
||||||
|
```
|
||||||
|
|
||||||
|
This will tell Swagger that `CommonBundle\Model\ValidationErrors` is returned when this endpoint returns a `400 Validation failed.` HTTP response.
|
||||||
|
|
||||||
|
__Note:__ You can omit the `200` entry in the `responseMap` property and specify the default `output` property instead. That will result on the same thing.
|
||||||
|
|
||||||
|
###wordnik/swagger-ui consumption...
|
||||||
|
|
||||||
|
You could import the routes for Swagger integration:
|
||||||
|
|
||||||
|
```yml
|
||||||
|
#app/config/routing.yml
|
||||||
|
|
||||||
|
nelmio_api_swagger:
|
||||||
|
resource: "@NelmioApiDocBundle/Resources/config/swagger_routing.yml"
|
||||||
|
prefix: /api-docs
|
||||||
|
```
|
||||||
|
|
||||||
|
Et voila!, simply specify http://yourdomain.com/api-docs in your Swagger client and you are good to go.
|
||||||
|
|
||||||
|
###Dump Swagger-compliant JSON to file-system...
|
||||||
|
|
||||||
|
The routes registered with the method above will read your `@ApiDoc` annotation during every request. Naturally, this will be slow because the bundle will parse your annotations every single time. For improved performance, you might be better off dumping the JSON output to the file-system and let your web-server serve them directly. If you want that, execute this command:
|
||||||
|
|
||||||
|
```
|
||||||
|
php app/console api:swagger:dump --all app/Resources/swagger-docs
|
||||||
|
```
|
||||||
|
|
||||||
|
The above command will dump JSON files under the `app/Resources/swagger-docs` directory (relative to your project root), and you can now process or server the files however you want. The destination defaults to the project root if not specified.
|
||||||
|
|
||||||
|
####Selective dumps
|
||||||
|
|
||||||
|
Dump the `api-docs.json` resource list file only:
|
||||||
|
```
|
||||||
|
php app/console api:swagger:dump --list-only
|
||||||
|
```
|
||||||
|
|
||||||
|
Dump a specific resource API declaration only:
|
||||||
|
```
|
||||||
|
php app/console api:swagger:dump --resource=users
|
||||||
|
```
|
||||||
|
The above command will dump the `/users` API declaration in an `users.json` file.
|
||||||
|
|
||||||
|
##Configuration reference
|
||||||
|
|
||||||
|
```yml
|
||||||
|
nelmio_api_doc:
|
||||||
|
swagger:
|
||||||
|
api_base_path: /api
|
||||||
|
swagger_version: 1.2
|
||||||
|
api_version: 0.1
|
||||||
|
info:
|
||||||
|
title: Symfony2
|
||||||
|
description: My awesome Symfony2 app!
|
||||||
|
TermsOfServiceUrl: ~
|
||||||
|
contact: ~
|
||||||
|
license: ~
|
||||||
|
licenseUrl: ~
|
||||||
|
```
|
@ -319,4 +319,41 @@ class ApiDocTest extends TestCase
|
|||||||
$this->assertTrue(is_array($array['tags']), 'Tags should be in array');
|
$this->assertTrue(is_array($array['tags']), 'Tags should be in array');
|
||||||
$this->assertEquals($data['tags'], $array['tags']);
|
$this->assertEquals($data['tags'], $array['tags']);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function testAlignmentOfOutputAndResponseModels()
|
||||||
|
{
|
||||||
|
$data = array(
|
||||||
|
'output' => 'FooBar',
|
||||||
|
'responseMap' => array(
|
||||||
|
400 => 'Foo\\ValidationErrorCollection',
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
$apiDoc = new ApiDoc($data);
|
||||||
|
|
||||||
|
$map = $apiDoc->getResponseMap();
|
||||||
|
|
||||||
|
$this->assertCount(2, $map);
|
||||||
|
$this->assertArrayHasKey(200, $map);
|
||||||
|
$this->assertArrayHasKey(400, $map);
|
||||||
|
$this->assertEquals($data['output'], $map[200]);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testAlignmentOfOutputAndResponseModels2()
|
||||||
|
{
|
||||||
|
$data = array(
|
||||||
|
'responseMap' => array(
|
||||||
|
200 => 'FooBar',
|
||||||
|
400 => 'Foo\\ValidationErrorCollection',
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
$apiDoc = new ApiDoc($data);
|
||||||
|
$map = $apiDoc->getResponseMap();
|
||||||
|
|
||||||
|
$this->assertCount(2, $map);
|
||||||
|
$this->assertArrayHasKey(200, $map);
|
||||||
|
$this->assertArrayHasKey(400, $map);
|
||||||
|
$this->assertEquals($apiDoc->getOutput(), $map[200]);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
66
Tests/Controller/ApiDocControllerTest.php
Normal file
66
Tests/Controller/ApiDocControllerTest.php
Normal file
@ -0,0 +1,66 @@
|
|||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* Created by PhpStorm.
|
||||||
|
* User: bezalelhermoso
|
||||||
|
* Date: 6/27/14
|
||||||
|
* Time: 4:35 PM
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace NelmioApiDocBundle\Tests\Controller;
|
||||||
|
use Nelmio\ApiDocBundle\Tests\WebTestCase;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Class ApiDocControllerTest
|
||||||
|
*
|
||||||
|
* @package NelmioApiDocBundle\Tests\Controller
|
||||||
|
* @author Bez Hermoso <bez@activelamp.com>
|
||||||
|
*/
|
||||||
|
class ApiDocControllerTest extends WebTestCase
|
||||||
|
{
|
||||||
|
public function testSwaggerDocResourceListRoute()
|
||||||
|
{
|
||||||
|
$client = static::createClient();
|
||||||
|
$client->request('GET', '/api-docs/');
|
||||||
|
|
||||||
|
$response = $client->getResponse();
|
||||||
|
|
||||||
|
$this->assertEquals(200, $response->getStatusCode());
|
||||||
|
$this->assertEquals('application/json', $response->headers->get('Content-type'));
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public function dataTestApiDeclarations()
|
||||||
|
{
|
||||||
|
return array(
|
||||||
|
array('resources'),
|
||||||
|
array('tests'),
|
||||||
|
array('tests2'),
|
||||||
|
array('TestResource'),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @dataProvider dataTestApiDeclarations
|
||||||
|
*/
|
||||||
|
public function testApiDeclarationRoutes($resource)
|
||||||
|
{
|
||||||
|
$client = static::createClient();
|
||||||
|
$client->request('GET', '/api-docs/' . $resource);
|
||||||
|
|
||||||
|
$response = $client->getResponse();
|
||||||
|
|
||||||
|
$this->assertEquals(200, $response->getStatusCode());
|
||||||
|
$this->assertEquals('application/json', $response->headers->get('Content-type'));
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testNonExistingApiDeclaration()
|
||||||
|
{
|
||||||
|
$client = static::createClient();
|
||||||
|
$client->request('GET', '/api-docs/santa');
|
||||||
|
|
||||||
|
$response = $client->getResponse();
|
||||||
|
$this->assertEquals(404, $response->getStatusCode());
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
@ -15,7 +15,7 @@ use Nelmio\ApiDocBundle\Tests\WebTestCase;
|
|||||||
|
|
||||||
class ApiDocExtractorTest extends WebTestCase
|
class ApiDocExtractorTest extends WebTestCase
|
||||||
{
|
{
|
||||||
const ROUTES_QUANTITY = 25;
|
const ROUTES_QUANTITY = 31;
|
||||||
|
|
||||||
public function testAll()
|
public function testAll()
|
||||||
{
|
{
|
||||||
@ -38,39 +38,39 @@ class ApiDocExtractorTest extends WebTestCase
|
|||||||
$this->assertNotNull($d['resource']);
|
$this->assertNotNull($d['resource']);
|
||||||
}
|
}
|
||||||
|
|
||||||
$a1 = $data[0]['annotation'];
|
$a1 = $data[7]['annotation'];
|
||||||
$array1 = $a1->toArray();
|
$array1 = $a1->toArray();
|
||||||
$this->assertTrue($a1->isResource());
|
$this->assertTrue($a1->isResource());
|
||||||
$this->assertEquals('index action', $a1->getDescription());
|
$this->assertEquals('index action', $a1->getDescription());
|
||||||
$this->assertTrue(is_array($array1['filters']));
|
$this->assertTrue(is_array($array1['filters']));
|
||||||
$this->assertNull($a1->getInput());
|
$this->assertNull($a1->getInput());
|
||||||
|
|
||||||
$a1 = $data[1]['annotation'];
|
$a1 = $data[7]['annotation'];
|
||||||
$array1 = $a1->toArray();
|
$array1 = $a1->toArray();
|
||||||
$this->assertTrue($a1->isResource());
|
$this->assertTrue($a1->isResource());
|
||||||
$this->assertEquals('index action', $a1->getDescription());
|
$this->assertEquals('index action', $a1->getDescription());
|
||||||
$this->assertTrue(is_array($array1['filters']));
|
$this->assertTrue(is_array($array1['filters']));
|
||||||
$this->assertNull($a1->getInput());
|
$this->assertNull($a1->getInput());
|
||||||
|
|
||||||
$a2 = $data[2]['annotation'];
|
$a2 = $data[8]['annotation'];
|
||||||
$array2 = $a2->toArray();
|
$array2 = $a2->toArray();
|
||||||
$this->assertFalse($a2->isResource());
|
$this->assertFalse($a2->isResource());
|
||||||
$this->assertEquals('create test', $a2->getDescription());
|
$this->assertEquals('create test', $a2->getDescription());
|
||||||
$this->assertFalse(isset($array2['filters']));
|
$this->assertFalse(isset($array2['filters']));
|
||||||
$this->assertEquals('Nelmio\ApiDocBundle\Tests\Fixtures\Form\TestType', $a2->getInput());
|
$this->assertEquals('Nelmio\ApiDocBundle\Tests\Fixtures\Form\TestType', $a2->getInput());
|
||||||
|
|
||||||
$a2 = $data[3]['annotation'];
|
$a2 = $data[9]['annotation'];
|
||||||
$array2 = $a2->toArray();
|
$array2 = $a2->toArray();
|
||||||
$this->assertFalse($a2->isResource());
|
$this->assertFalse($a2->isResource());
|
||||||
$this->assertEquals('create test', $a2->getDescription());
|
$this->assertEquals('create test', $a2->getDescription());
|
||||||
$this->assertFalse(isset($array2['filters']));
|
$this->assertFalse(isset($array2['filters']));
|
||||||
$this->assertEquals('Nelmio\ApiDocBundle\Tests\Fixtures\Form\TestType', $a2->getInput());
|
$this->assertEquals('Nelmio\ApiDocBundle\Tests\Fixtures\Form\TestType', $a2->getInput());
|
||||||
|
|
||||||
$a4 = $data[5]['annotation'];
|
$a4 = $data[11]['annotation'];
|
||||||
$this->assertTrue($a4->isResource());
|
$this->assertTrue($a4->isResource());
|
||||||
$this->assertEquals('TestResource', $a4->getResource());
|
$this->assertEquals('TestResource', $a4->getResource());
|
||||||
|
|
||||||
$a3 = $data['14']['annotation'];
|
$a3 = $data[20]['annotation'];
|
||||||
$this->assertTrue($a3->getHttps());
|
$this->assertTrue($a3->getHttps());
|
||||||
|
|
||||||
}
|
}
|
||||||
@ -224,6 +224,7 @@ class ApiDocExtractorTest extends WebTestCase
|
|||||||
|
|
||||||
$this->assertNotNull($annotation);
|
$this->assertNotNull($annotation);
|
||||||
$output = $annotation->getOutput();
|
$output = $annotation->getOutput();
|
||||||
|
|
||||||
$parsers = $output['parsers'];
|
$parsers = $output['parsers'];
|
||||||
$this->assertEquals(
|
$this->assertEquals(
|
||||||
"Nelmio\\ApiDocBundle\\Parser\\JmsMetadataParser",
|
"Nelmio\\ApiDocBundle\\Parser\\JmsMetadataParser",
|
||||||
|
81
Tests/Fixtures/Controller/ResourceController.php
Normal file
81
Tests/Fixtures/Controller/ResourceController.php
Normal file
@ -0,0 +1,81 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This file is part of the NelmioApiDocBundle.
|
||||||
|
*
|
||||||
|
* (c) Nelmio <hello@nelm.io>
|
||||||
|
*
|
||||||
|
* For the full copyright and license information, please view the LICENSE
|
||||||
|
* file that was distributed with this source code.
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace Nelmio\ApiDocBundle\Tests\Fixtures\Controller;
|
||||||
|
use Nelmio\ApiDocBundle\Annotation\ApiDoc;
|
||||||
|
|
||||||
|
class ResourceController
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @ApiDoc(
|
||||||
|
* resource=true,
|
||||||
|
* resourceDescription="Operations on resource.",
|
||||||
|
* description="List resources.",
|
||||||
|
* statusCodes={200 = "Returned on success.", 404 = "Returned if resource cannot be found."}
|
||||||
|
* )
|
||||||
|
*/
|
||||||
|
public function listResourcesAction()
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @ApiDoc(description="Retrieve a resource by ID.")
|
||||||
|
*/
|
||||||
|
public function getResourceAction()
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @ApiDoc(description="Delete a resource by ID.")
|
||||||
|
*/
|
||||||
|
public function deleteResourceAction()
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @ApiDoc(
|
||||||
|
* description="Create a new resource.",
|
||||||
|
* input={"class" = "Nelmio\ApiDocBundle\Tests\Fixtures\Form\SimpleType", "name" = ""},
|
||||||
|
* output="Nelmio\ApiDocBundle\Tests\Fixtures\Model\JmsNested"
|
||||||
|
* )
|
||||||
|
*/
|
||||||
|
public function createResourceAction()
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @ApiDoc(resource=true, description="List another resource.", resourceDescription="Operations on another resource.")
|
||||||
|
*/
|
||||||
|
public function listAnotherResourcesAction()
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @ApiDoc(description="Retrieve another resource by ID.")
|
||||||
|
*/
|
||||||
|
public function getAnotherResourceAction()
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @ApiDoc(description="Update a resource bu ID.")
|
||||||
|
*/
|
||||||
|
public function updateAnotherResourceAction()
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
@ -1,6 +1,7 @@
|
|||||||
<?php
|
<?php
|
||||||
|
|
||||||
namespace Nelmio\ApiDocBundle\Tests\Fixtures\Form;
|
namespace Nelmio\ApiDocBundle\Tests\Fixtures\Form;
|
||||||
|
|
||||||
use Symfony\Component\Form\AbstractType;
|
use Symfony\Component\Form\AbstractType;
|
||||||
use Symfony\Component\Form\FormBuilderInterface;
|
use Symfony\Component\Form\FormBuilderInterface;
|
||||||
|
|
||||||
|
@ -54,3 +54,14 @@ jms_serializer:
|
|||||||
|
|
||||||
nelmio_api_doc:
|
nelmio_api_doc:
|
||||||
exclude_sections: ["private", "exclusive"]
|
exclude_sections: ["private", "exclusive"]
|
||||||
|
swagger:
|
||||||
|
api_base_path: /api
|
||||||
|
swagger_version: 1.2
|
||||||
|
api_version: 3.14
|
||||||
|
info:
|
||||||
|
title: Nelmio Swagger
|
||||||
|
description: Testing Swagger integration.
|
||||||
|
TermsOfServiceUrl: https://github.com
|
||||||
|
contact: user@domain.tld
|
||||||
|
license: MIT
|
||||||
|
licenseUrl: http://opensource.org/licenses/MIT
|
||||||
|
@ -161,3 +161,49 @@ test_route_22:
|
|||||||
defaults: { _controller: NelmioApiDocTestBundle:Test:zActionWithNullableRequestParam }
|
defaults: { _controller: NelmioApiDocTestBundle:Test:zActionWithNullableRequestParam }
|
||||||
requirements:
|
requirements:
|
||||||
_method: POST
|
_method: POST
|
||||||
|
|
||||||
|
test_route_list_resource:
|
||||||
|
pattern: /api/resources.{_format}
|
||||||
|
defaults: { _controller: NelmioApiDocTestBundle:Resource:listResources, _format: json }
|
||||||
|
requirements:
|
||||||
|
_method: GET
|
||||||
|
_format: json|xml|html
|
||||||
|
|
||||||
|
test_route_get_resource:
|
||||||
|
pattern: /api/resources/{id}.{_format}
|
||||||
|
defaults: { _controller: NelmioApiDocTestBundle:Resource:getResource, _format: json }
|
||||||
|
requirements:
|
||||||
|
_method: GET
|
||||||
|
_format: json|xml|html
|
||||||
|
|
||||||
|
test_route_delete_resource:
|
||||||
|
pattern: /api/resources/{id}.{_format}
|
||||||
|
defaults: { _controller: NelmioApiDocTestBundle:Resource:deleteResource, _format: json }
|
||||||
|
requirements:
|
||||||
|
_method: DELETE
|
||||||
|
_format: json|xml|html
|
||||||
|
|
||||||
|
test_route_create_resource:
|
||||||
|
pattern: /api/resources.{_format}
|
||||||
|
defaults: { _controller: NelmioApiDocTestBundle:Resource:createResource, _format: json }
|
||||||
|
requirements:
|
||||||
|
_method: POST
|
||||||
|
_format: json|xml|html
|
||||||
|
|
||||||
|
test_route_list_another_resource:
|
||||||
|
pattern: /api/other-resources.{_format}
|
||||||
|
defaults: { _controller: NelmioApiDocTestBundle:Resource:listAnotherResources, _format: json }
|
||||||
|
requirements:
|
||||||
|
_method: GET
|
||||||
|
_format: json|xml|html
|
||||||
|
|
||||||
|
test_route_update_another_resource:
|
||||||
|
pattern: /api/other-resources/{id}.{_format}
|
||||||
|
defaults: { _controller: NelmioApiDocTestBundle:Resource:updateAnotherResource, _format: json }
|
||||||
|
requirements:
|
||||||
|
_method: PUT|PATCH
|
||||||
|
_format: json|xml|html
|
||||||
|
|
||||||
|
swagger_doc:
|
||||||
|
resource: @NelmioApiDocBundle/Resources/config/swagger_routing.yml
|
||||||
|
prefix: /api-docs
|
@ -26,6 +26,241 @@ class MarkdownFormatterTest extends WebTestCase
|
|||||||
$result = $container->get('nelmio_api_doc.formatter.markdown_formatter')->format($data);
|
$result = $container->get('nelmio_api_doc.formatter.markdown_formatter')->format($data);
|
||||||
|
|
||||||
$expected = <<<MARKDOWN
|
$expected = <<<MARKDOWN
|
||||||
|
## /api/other-resources ##
|
||||||
|
|
||||||
|
### `GET` /api/other-resources.{_format} ###
|
||||||
|
|
||||||
|
_List another resource._
|
||||||
|
|
||||||
|
#### Requirements ####
|
||||||
|
|
||||||
|
**_format**
|
||||||
|
|
||||||
|
- Requirement: json|xml|html
|
||||||
|
|
||||||
|
|
||||||
|
### `PUT|PATCH` /api/other-resources/{id}.{_format} ###
|
||||||
|
|
||||||
|
_Update a resource bu ID._
|
||||||
|
|
||||||
|
#### Requirements ####
|
||||||
|
|
||||||
|
**_format**
|
||||||
|
|
||||||
|
- Requirement: json|xml|html
|
||||||
|
**id**
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## /api/resources ##
|
||||||
|
|
||||||
|
### `GET` /api/resources.{_format} ###
|
||||||
|
|
||||||
|
_List resources._
|
||||||
|
|
||||||
|
#### Requirements ####
|
||||||
|
|
||||||
|
**_format**
|
||||||
|
|
||||||
|
- Requirement: json|xml|html
|
||||||
|
|
||||||
|
|
||||||
|
### `POST` /api/resources.{_format} ###
|
||||||
|
|
||||||
|
_Create a new resource._
|
||||||
|
|
||||||
|
#### Requirements ####
|
||||||
|
|
||||||
|
**_format**
|
||||||
|
|
||||||
|
- Requirement: json|xml|html
|
||||||
|
|
||||||
|
#### Parameters ####
|
||||||
|
|
||||||
|
a:
|
||||||
|
|
||||||
|
* type: string
|
||||||
|
* required: true
|
||||||
|
* description: Something that describes A.
|
||||||
|
|
||||||
|
b:
|
||||||
|
|
||||||
|
* type: float
|
||||||
|
* required: true
|
||||||
|
|
||||||
|
c:
|
||||||
|
|
||||||
|
* type: choice
|
||||||
|
* required: true
|
||||||
|
|
||||||
|
d:
|
||||||
|
|
||||||
|
* type: datetime
|
||||||
|
* required: true
|
||||||
|
|
||||||
|
e:
|
||||||
|
|
||||||
|
* type: date
|
||||||
|
* required: true
|
||||||
|
|
||||||
|
g:
|
||||||
|
|
||||||
|
* type: string
|
||||||
|
* required: true
|
||||||
|
|
||||||
|
#### Response ####
|
||||||
|
|
||||||
|
foo:
|
||||||
|
|
||||||
|
* type: DateTime
|
||||||
|
|
||||||
|
bar:
|
||||||
|
|
||||||
|
* type: string
|
||||||
|
|
||||||
|
baz[]:
|
||||||
|
|
||||||
|
* type: array of integers
|
||||||
|
* description: Epic description.
|
||||||
|
|
||||||
|
With multiple lines.
|
||||||
|
|
||||||
|
circular:
|
||||||
|
|
||||||
|
* type: object (JmsNested)
|
||||||
|
|
||||||
|
circular[foo]:
|
||||||
|
|
||||||
|
* type: DateTime
|
||||||
|
|
||||||
|
circular[bar]:
|
||||||
|
|
||||||
|
* type: string
|
||||||
|
|
||||||
|
circular[baz][]:
|
||||||
|
|
||||||
|
* type: array of integers
|
||||||
|
* description: Epic description.
|
||||||
|
|
||||||
|
With multiple lines.
|
||||||
|
|
||||||
|
circular[circular]:
|
||||||
|
|
||||||
|
* type: object (JmsNested)
|
||||||
|
|
||||||
|
circular[parent]:
|
||||||
|
|
||||||
|
* type: object (JmsTest)
|
||||||
|
|
||||||
|
circular[parent][foo]:
|
||||||
|
|
||||||
|
* type: string
|
||||||
|
|
||||||
|
circular[parent][bar]:
|
||||||
|
|
||||||
|
* type: DateTime
|
||||||
|
|
||||||
|
circular[parent][number]:
|
||||||
|
|
||||||
|
* type: double
|
||||||
|
|
||||||
|
circular[parent][arr]:
|
||||||
|
|
||||||
|
* type: array
|
||||||
|
|
||||||
|
circular[parent][nested]:
|
||||||
|
|
||||||
|
* type: object (JmsNested)
|
||||||
|
|
||||||
|
circular[parent][nested_array][]:
|
||||||
|
|
||||||
|
* type: array of objects (JmsNested)
|
||||||
|
|
||||||
|
circular[since]:
|
||||||
|
|
||||||
|
* type: string
|
||||||
|
* versions: >=0.2
|
||||||
|
|
||||||
|
circular[until]:
|
||||||
|
|
||||||
|
* type: string
|
||||||
|
* versions: <=0.3
|
||||||
|
|
||||||
|
circular[since_and_until]:
|
||||||
|
|
||||||
|
* type: string
|
||||||
|
* versions: >=0.4,<=0.5
|
||||||
|
|
||||||
|
parent:
|
||||||
|
|
||||||
|
* type: object (JmsTest)
|
||||||
|
|
||||||
|
parent[foo]:
|
||||||
|
|
||||||
|
* type: string
|
||||||
|
|
||||||
|
parent[bar]:
|
||||||
|
|
||||||
|
* type: DateTime
|
||||||
|
|
||||||
|
parent[number]:
|
||||||
|
|
||||||
|
* type: double
|
||||||
|
|
||||||
|
parent[arr]:
|
||||||
|
|
||||||
|
* type: array
|
||||||
|
|
||||||
|
parent[nested]:
|
||||||
|
|
||||||
|
* type: object (JmsNested)
|
||||||
|
|
||||||
|
parent[nested_array][]:
|
||||||
|
|
||||||
|
* type: array of objects (JmsNested)
|
||||||
|
|
||||||
|
since:
|
||||||
|
|
||||||
|
* type: string
|
||||||
|
* versions: >=0.2
|
||||||
|
|
||||||
|
until:
|
||||||
|
|
||||||
|
* type: string
|
||||||
|
* versions: <=0.3
|
||||||
|
|
||||||
|
since_and_until:
|
||||||
|
|
||||||
|
* type: string
|
||||||
|
* versions: >=0.4,<=0.5
|
||||||
|
|
||||||
|
|
||||||
|
### `GET` /api/resources/{id}.{_format} ###
|
||||||
|
|
||||||
|
_Retrieve a resource by ID._
|
||||||
|
|
||||||
|
#### Requirements ####
|
||||||
|
|
||||||
|
**_format**
|
||||||
|
|
||||||
|
- Requirement: json|xml|html
|
||||||
|
**id**
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
### `DELETE` /api/resources/{id}.{_format} ###
|
||||||
|
|
||||||
|
_Delete a resource by ID._
|
||||||
|
|
||||||
|
#### Requirements ####
|
||||||
|
|
||||||
|
**_format**
|
||||||
|
|
||||||
|
- Requirement: json|xml|html
|
||||||
|
**id**
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
## /tests ##
|
## /tests ##
|
||||||
|
|
||||||
### `GET` /tests.{_format} ###
|
### `GET` /tests.{_format} ###
|
||||||
@ -431,7 +666,7 @@ nested_array[]:
|
|||||||
|
|
||||||
**id**
|
**id**
|
||||||
|
|
||||||
- Requirement: \\d+
|
- Requirement: \d+
|
||||||
|
|
||||||
|
|
||||||
### `GET` /z-action-with-deprecated-indicator ###
|
### `GET` /z-action-with-deprecated-indicator ###
|
||||||
@ -459,7 +694,7 @@ param1:
|
|||||||
|
|
||||||
page:
|
page:
|
||||||
|
|
||||||
* Requirement: \\d+
|
* Requirement: \d+
|
||||||
* Description: Page of the overview.
|
* Description: Page of the overview.
|
||||||
* Default: 1
|
* Default: 1
|
||||||
|
|
||||||
|
@ -198,7 +198,6 @@ class SimpleFormatterTest extends WebTestCase
|
|||||||
'dataType' => 'string',
|
'dataType' => 'string',
|
||||||
'actualType' => DataTypes::STRING,
|
'actualType' => DataTypes::STRING,
|
||||||
'subType' => null,
|
'subType' => null,
|
||||||
'default' => null,
|
|
||||||
'default' => "DefaultTest",
|
'default' => "DefaultTest",
|
||||||
'required' => true,
|
'required' => true,
|
||||||
'description' => '',
|
'description' => '',
|
||||||
@ -622,8 +621,6 @@ And, it supports multilines until the first \'@\' char.',
|
|||||||
),
|
),
|
||||||
),
|
),
|
||||||
'https' => false,
|
'https' => false,
|
||||||
'description' => 'This method is useful to test if the getDocComment works.',
|
|
||||||
'documentation' => "This method is useful to test if the getDocComment works.\nAnd, it supports multilines until the first '@' char.",
|
|
||||||
'authentication' => false,
|
'authentication' => false,
|
||||||
'authenticationRoles' => array(),
|
'authenticationRoles' => array(),
|
||||||
'deprecated' => false,
|
'deprecated' => false,
|
||||||
@ -1069,39 +1066,39 @@ With multiple lines.',
|
|||||||
'subType' => null,
|
'subType' => null,
|
||||||
'default' => null,
|
'default' => null,
|
||||||
'required' => null,
|
'required' => null,
|
||||||
'readonly' => null
|
'readonly' => null,
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
),
|
),
|
||||||
'related' => array(
|
'related' => array(
|
||||||
'dataType' => 'object (Test)',
|
'dataType' => 'object (Test)',
|
||||||
|
'actualType' => DataTypes::MODEL,
|
||||||
|
'subType' => 'Nelmio\ApiDocBundle\Tests\Fixtures\Model\Test',
|
||||||
|
'default' => null,
|
||||||
'readonly' => false,
|
'readonly' => false,
|
||||||
'required' => false,
|
'required' => false,
|
||||||
'description' => '',
|
'description' => '',
|
||||||
'sinceVersion' => null,
|
'sinceVersion' => null,
|
||||||
'untilVersion' => null,
|
'untilVersion' => null,
|
||||||
'actualType' => DataTypes::MODEL,
|
|
||||||
'subType' => 'Nelmio\ApiDocBundle\Tests\Fixtures\Model\Test',
|
|
||||||
'default' => null,
|
|
||||||
'children' => array(
|
'children' => array(
|
||||||
'a' => array(
|
'a' => array(
|
||||||
'dataType' => 'string',
|
'dataType' => 'string',
|
||||||
'format' => '{length: min: foo}, {not blank}',
|
|
||||||
'required' => true,
|
|
||||||
'readonly' => null,
|
|
||||||
'actualType' => DataTypes::STRING,
|
'actualType' => DataTypes::STRING,
|
||||||
'subType' => null,
|
'subType' => null,
|
||||||
'default' => 'nelmio',
|
'default' => 'nelmio',
|
||||||
|
'format' => '{length: min: foo}, {not blank}',
|
||||||
|
'required' => true,
|
||||||
|
'readonly' => null
|
||||||
),
|
),
|
||||||
'b' => array(
|
'b' => array(
|
||||||
'dataType' => 'DateTime',
|
'dataType' => 'DateTime',
|
||||||
'required' => null,
|
|
||||||
'readonly' => null,
|
|
||||||
'actualType' => DataTypes::DATETIME,
|
'actualType' => DataTypes::DATETIME,
|
||||||
'subType' => null,
|
'subType' => null,
|
||||||
'default' => null,
|
'default' => null,
|
||||||
|
'required' => null,
|
||||||
|
'readonly' => null
|
||||||
)
|
)
|
||||||
)
|
),
|
||||||
)
|
)
|
||||||
),
|
),
|
||||||
'authenticationRoles' => array(),
|
'authenticationRoles' => array(),
|
||||||
@ -1147,7 +1144,6 @@ With multiple lines.',
|
|||||||
'dataType' => 'string',
|
'dataType' => 'string',
|
||||||
'actualType' => DataTypes::STRING,
|
'actualType' => DataTypes::STRING,
|
||||||
'subType' => null,
|
'subType' => null,
|
||||||
'default' => null,
|
|
||||||
'default' => "DefaultTest",
|
'default' => "DefaultTest",
|
||||||
'required' => true,
|
'required' => true,
|
||||||
'description' => '',
|
'description' => '',
|
||||||
@ -1211,33 +1207,33 @@ With multiple lines.',
|
|||||||
),
|
),
|
||||||
'related' => array(
|
'related' => array(
|
||||||
'dataType' => 'object (Test)',
|
'dataType' => 'object (Test)',
|
||||||
|
'subType' => 'Nelmio\ApiDocBundle\Tests\Fixtures\Model\Test',
|
||||||
|
'actualType' => DataTypes::MODEL,
|
||||||
|
'default' => null,
|
||||||
'readonly' => false,
|
'readonly' => false,
|
||||||
'required' => false,
|
'required' => false,
|
||||||
'description' => '',
|
'description' => '',
|
||||||
'sinceVersion' => null,
|
'sinceVersion' => null,
|
||||||
'untilVersion' => null,
|
'untilVersion' => null,
|
||||||
'actualType' => DataTypes::MODEL,
|
|
||||||
'subType' => 'Nelmio\ApiDocBundle\Tests\Fixtures\Model\Test',
|
|
||||||
'default' => null,
|
|
||||||
'children' => array(
|
'children' => array(
|
||||||
'a' => array(
|
'a' => array(
|
||||||
'dataType' => 'string',
|
'dataType' => 'string',
|
||||||
'format' => '{length: min: foo}, {not blank}',
|
|
||||||
'required' => true,
|
|
||||||
'readonly' => null,
|
|
||||||
'actualType' => DataTypes::STRING,
|
'actualType' => DataTypes::STRING,
|
||||||
'subType' => null,
|
'subType' => null,
|
||||||
'default' => 'nelmio',
|
'default' => 'nelmio',
|
||||||
|
'format' => '{length: min: foo}, {not blank}',
|
||||||
|
'required' => true,
|
||||||
|
'readonly' => null
|
||||||
),
|
),
|
||||||
'b' => array(
|
'b' => array(
|
||||||
'dataType' => 'DateTime',
|
'dataType' => 'DateTime',
|
||||||
'required' => null,
|
|
||||||
'readonly' => null,
|
|
||||||
'actualType' => DataTypes::DATETIME,
|
'actualType' => DataTypes::DATETIME,
|
||||||
'subType' => null,
|
'subType' => null,
|
||||||
'default' => null,
|
'default' => null,
|
||||||
|
'required' => null,
|
||||||
|
'readonly' => null
|
||||||
)
|
)
|
||||||
)
|
),
|
||||||
)
|
)
|
||||||
),
|
),
|
||||||
'authenticationRoles' => array(),
|
'authenticationRoles' => array(),
|
||||||
@ -1245,7 +1241,6 @@ With multiple lines.',
|
|||||||
),
|
),
|
||||||
'/tests2' =>
|
'/tests2' =>
|
||||||
array(
|
array(
|
||||||
0 =>
|
|
||||||
array(
|
array(
|
||||||
'method' => 'POST',
|
'method' => 'POST',
|
||||||
'uri' => '/tests2.{_format}',
|
'uri' => '/tests2.{_format}',
|
||||||
@ -1267,7 +1262,6 @@ With multiple lines.',
|
|||||||
),
|
),
|
||||||
'/tests2' =>
|
'/tests2' =>
|
||||||
array(
|
array(
|
||||||
0 =>
|
|
||||||
array(
|
array(
|
||||||
'method' => 'POST',
|
'method' => 'POST',
|
||||||
'uri' => '/tests2.{_format}',
|
'uri' => '/tests2.{_format}',
|
||||||
@ -1299,6 +1293,573 @@ With multiple lines.',
|
|||||||
'deprecated' => false,
|
'deprecated' => false,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
'/api/other-resources' =>
|
||||||
|
array(
|
||||||
|
array(
|
||||||
|
'method' => 'GET',
|
||||||
|
'uri' => '/api/other-resources.{_format}',
|
||||||
|
'description' => 'List another resource.',
|
||||||
|
'requirements' =>
|
||||||
|
array(
|
||||||
|
'_format' =>
|
||||||
|
array(
|
||||||
|
'requirement' => 'json|xml|html',
|
||||||
|
'dataType' => '',
|
||||||
|
'description' => '',
|
||||||
|
),
|
||||||
|
),
|
||||||
|
'resourceDescription' => 'Operations on another resource.',
|
||||||
|
'https' => false,
|
||||||
|
'authentication' => false,
|
||||||
|
'authenticationRoles' =>
|
||||||
|
array(),
|
||||||
|
'deprecated' => false,
|
||||||
|
),
|
||||||
|
array(
|
||||||
|
'method' => 'PUT|PATCH',
|
||||||
|
'uri' => '/api/other-resources/{id}.{_format}',
|
||||||
|
'description' => 'Update a resource bu ID.',
|
||||||
|
'requirements' =>
|
||||||
|
array(
|
||||||
|
'_format' =>
|
||||||
|
array(
|
||||||
|
'requirement' => 'json|xml|html',
|
||||||
|
'dataType' => '',
|
||||||
|
'description' => '',
|
||||||
|
),
|
||||||
|
'id' =>
|
||||||
|
array(
|
||||||
|
'requirement' => '',
|
||||||
|
'dataType' => '',
|
||||||
|
'description' => '',
|
||||||
|
),
|
||||||
|
),
|
||||||
|
'https' => false,
|
||||||
|
'authentication' => false,
|
||||||
|
'authenticationRoles' =>
|
||||||
|
array(),
|
||||||
|
'deprecated' => false,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
'/api/resources' =>
|
||||||
|
array(
|
||||||
|
array(
|
||||||
|
'method' => 'GET',
|
||||||
|
'uri' => '/api/resources.{_format}',
|
||||||
|
'description' => 'List resources.',
|
||||||
|
'requirements' =>
|
||||||
|
array(
|
||||||
|
'_format' =>
|
||||||
|
array(
|
||||||
|
'requirement' => 'json|xml|html',
|
||||||
|
'dataType' => '',
|
||||||
|
'description' => '',
|
||||||
|
),
|
||||||
|
),
|
||||||
|
'statusCodes' =>
|
||||||
|
array(
|
||||||
|
200 =>
|
||||||
|
array(
|
||||||
|
'Returned on success.',
|
||||||
|
),
|
||||||
|
404 =>
|
||||||
|
array(
|
||||||
|
'Returned if resource cannot be found.',
|
||||||
|
),
|
||||||
|
),
|
||||||
|
'resourceDescription' => 'Operations on resource.',
|
||||||
|
'https' => false,
|
||||||
|
'authentication' => false,
|
||||||
|
'authenticationRoles' =>
|
||||||
|
array(),
|
||||||
|
'deprecated' => false,
|
||||||
|
),
|
||||||
|
array(
|
||||||
|
'method' => 'POST',
|
||||||
|
'uri' => '/api/resources.{_format}',
|
||||||
|
'description' => 'Create a new resource.',
|
||||||
|
'parameters' =>
|
||||||
|
array(
|
||||||
|
'a' =>
|
||||||
|
array(
|
||||||
|
'dataType' => 'string',
|
||||||
|
'actualType' => 'string',
|
||||||
|
'subType' => NULL,
|
||||||
|
'required' => true,
|
||||||
|
'description' => 'Something that describes A.',
|
||||||
|
'readonly' => false,
|
||||||
|
'default' => null,
|
||||||
|
),
|
||||||
|
'b' =>
|
||||||
|
array(
|
||||||
|
'dataType' => 'float',
|
||||||
|
'actualType' => 'float',
|
||||||
|
'subType' => NULL,
|
||||||
|
'required' => true,
|
||||||
|
'description' => '',
|
||||||
|
'readonly' => false,
|
||||||
|
'default' => null,
|
||||||
|
),
|
||||||
|
'c' =>
|
||||||
|
array(
|
||||||
|
'dataType' => 'choice',
|
||||||
|
'actualType' => 'choice',
|
||||||
|
'subType' => NULL,
|
||||||
|
'required' => true,
|
||||||
|
'description' => '',
|
||||||
|
'readonly' => false,
|
||||||
|
'format' => '{"x":"X","y":"Y","z":"Z"}',
|
||||||
|
'default' => null,
|
||||||
|
),
|
||||||
|
'd' =>
|
||||||
|
array(
|
||||||
|
'dataType' => 'datetime',
|
||||||
|
'actualType' => 'datetime',
|
||||||
|
'subType' => NULL,
|
||||||
|
'required' => true,
|
||||||
|
'description' => '',
|
||||||
|
'readonly' => false,
|
||||||
|
'default' => null,
|
||||||
|
),
|
||||||
|
'e' =>
|
||||||
|
array(
|
||||||
|
'dataType' => 'date',
|
||||||
|
'actualType' => 'date',
|
||||||
|
'subType' => NULL,
|
||||||
|
'required' => true,
|
||||||
|
'description' => '',
|
||||||
|
'readonly' => false,
|
||||||
|
'default' => null,
|
||||||
|
),
|
||||||
|
'g' =>
|
||||||
|
array(
|
||||||
|
'dataType' => 'string',
|
||||||
|
'actualType' => 'string',
|
||||||
|
'subType' => NULL,
|
||||||
|
'required' => true,
|
||||||
|
'description' => '',
|
||||||
|
'readonly' => false,
|
||||||
|
'default' => null,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
'requirements' =>
|
||||||
|
array(
|
||||||
|
'_format' =>
|
||||||
|
array(
|
||||||
|
'requirement' => 'json|xml|html',
|
||||||
|
'dataType' => '',
|
||||||
|
'description' => '',
|
||||||
|
),
|
||||||
|
),
|
||||||
|
'response' =>
|
||||||
|
array(
|
||||||
|
'foo' =>
|
||||||
|
array(
|
||||||
|
'dataType' => 'DateTime',
|
||||||
|
'actualType' => 'datetime',
|
||||||
|
'default' => null,
|
||||||
|
'subType' => NULL,
|
||||||
|
'required' => false,
|
||||||
|
'description' => '',
|
||||||
|
'readonly' => true,
|
||||||
|
'sinceVersion' => NULL,
|
||||||
|
'untilVersion' => NULL,
|
||||||
|
),
|
||||||
|
'bar' =>
|
||||||
|
array(
|
||||||
|
'dataType' => 'string',
|
||||||
|
'actualType' => 'string',
|
||||||
|
'default' => 'baz',
|
||||||
|
'subType' => NULL,
|
||||||
|
'required' => false,
|
||||||
|
'description' => '',
|
||||||
|
'readonly' => false,
|
||||||
|
'sinceVersion' => NULL,
|
||||||
|
'untilVersion' => NULL,
|
||||||
|
),
|
||||||
|
'baz' =>
|
||||||
|
array(
|
||||||
|
'dataType' => 'array of integers',
|
||||||
|
'actualType' => 'collection',
|
||||||
|
'subType' => 'integer',
|
||||||
|
'required' => false,
|
||||||
|
'description' => 'Epic description.
|
||||||
|
|
||||||
|
With multiple lines.',
|
||||||
|
'readonly' => false,
|
||||||
|
'sinceVersion' => NULL,
|
||||||
|
'default' => null,
|
||||||
|
'untilVersion' => NULL,
|
||||||
|
),
|
||||||
|
'circular' =>
|
||||||
|
array(
|
||||||
|
'dataType' => 'object (JmsNested)',
|
||||||
|
'actualType' => 'model',
|
||||||
|
'default' => null,
|
||||||
|
'subType' => 'Nelmio\\ApiDocBundle\\Tests\\Fixtures\\Model\\JmsNested',
|
||||||
|
'required' => false,
|
||||||
|
'description' => '',
|
||||||
|
'readonly' => false,
|
||||||
|
'sinceVersion' => NULL,
|
||||||
|
'untilVersion' => NULL,
|
||||||
|
'children' =>
|
||||||
|
array(
|
||||||
|
'foo' =>
|
||||||
|
array(
|
||||||
|
'dataType' => 'DateTime',
|
||||||
|
'actualType' => 'datetime',
|
||||||
|
'default' => null,
|
||||||
|
'subType' => NULL,
|
||||||
|
'required' => false,
|
||||||
|
'description' => '',
|
||||||
|
'readonly' => true,
|
||||||
|
'sinceVersion' => NULL,
|
||||||
|
'untilVersion' => NULL,
|
||||||
|
),
|
||||||
|
'bar' =>
|
||||||
|
array(
|
||||||
|
'dataType' => 'string',
|
||||||
|
'actualType' => 'string',
|
||||||
|
'subType' => NULL,
|
||||||
|
'default' => 'baz',
|
||||||
|
'required' => false,
|
||||||
|
'description' => '',
|
||||||
|
'readonly' => false,
|
||||||
|
'sinceVersion' => NULL,
|
||||||
|
'untilVersion' => NULL,
|
||||||
|
),
|
||||||
|
'baz' =>
|
||||||
|
array(
|
||||||
|
'dataType' => 'array of integers',
|
||||||
|
'actualType' => 'collection',
|
||||||
|
'subType' => 'integer',
|
||||||
|
'required' => false,
|
||||||
|
'description' => 'Epic description.
|
||||||
|
|
||||||
|
With multiple lines.',
|
||||||
|
'readonly' => false,
|
||||||
|
'sinceVersion' => NULL,
|
||||||
|
'untilVersion' => NULL,
|
||||||
|
'default' => null,
|
||||||
|
),
|
||||||
|
'circular' =>
|
||||||
|
array(
|
||||||
|
'dataType' => 'object (JmsNested)',
|
||||||
|
'actualType' => 'model',
|
||||||
|
'default' => null,
|
||||||
|
'subType' => 'Nelmio\\ApiDocBundle\\Tests\\Fixtures\\Model\\JmsNested',
|
||||||
|
'required' => false,
|
||||||
|
'description' => '',
|
||||||
|
'readonly' => false,
|
||||||
|
'sinceVersion' => NULL,
|
||||||
|
'untilVersion' => NULL,
|
||||||
|
),
|
||||||
|
'parent' =>
|
||||||
|
array(
|
||||||
|
'dataType' => 'object (JmsTest)',
|
||||||
|
'actualType' => 'model',
|
||||||
|
'default' => null,
|
||||||
|
'subType' => 'Nelmio\\ApiDocBundle\\Tests\\Fixtures\\Model\\JmsTest',
|
||||||
|
'required' => false,
|
||||||
|
'description' => '',
|
||||||
|
'readonly' => false,
|
||||||
|
'sinceVersion' => NULL,
|
||||||
|
'untilVersion' => NULL,
|
||||||
|
'children' =>
|
||||||
|
array(
|
||||||
|
'foo' =>
|
||||||
|
array(
|
||||||
|
'dataType' => 'string',
|
||||||
|
'actualType' => 'string',
|
||||||
|
'subType' => NULL,
|
||||||
|
'required' => false,
|
||||||
|
'description' => '',
|
||||||
|
'readonly' => false,
|
||||||
|
'sinceVersion' => NULL,
|
||||||
|
'untilVersion' => NULL,
|
||||||
|
'default' => null,
|
||||||
|
),
|
||||||
|
'bar' =>
|
||||||
|
array(
|
||||||
|
'dataType' => 'DateTime',
|
||||||
|
'actualType' => 'datetime',
|
||||||
|
'subType' => NULL,
|
||||||
|
'required' => false,
|
||||||
|
'description' => '',
|
||||||
|
'readonly' => true,
|
||||||
|
'sinceVersion' => NULL,
|
||||||
|
'untilVersion' => NULL,
|
||||||
|
'default' => null,
|
||||||
|
),
|
||||||
|
'number' =>
|
||||||
|
array(
|
||||||
|
'dataType' => 'double',
|
||||||
|
'actualType' => 'float',
|
||||||
|
'subType' => NULL,
|
||||||
|
'required' => false,
|
||||||
|
'description' => '',
|
||||||
|
'readonly' => false,
|
||||||
|
'sinceVersion' => NULL,
|
||||||
|
'untilVersion' => NULL,
|
||||||
|
'default' => null,
|
||||||
|
),
|
||||||
|
'arr' =>
|
||||||
|
array(
|
||||||
|
'dataType' => 'array',
|
||||||
|
'actualType' => 'collection',
|
||||||
|
'subType' => NULL,
|
||||||
|
'required' => false,
|
||||||
|
'description' => '',
|
||||||
|
'readonly' => false,
|
||||||
|
'sinceVersion' => NULL,
|
||||||
|
'untilVersion' => NULL,
|
||||||
|
'default' => null,
|
||||||
|
),
|
||||||
|
'nested' =>
|
||||||
|
array(
|
||||||
|
'dataType' => 'object (JmsNested)',
|
||||||
|
'actualType' => 'model',
|
||||||
|
'default' => null,
|
||||||
|
'subType' => 'Nelmio\\ApiDocBundle\\Tests\\Fixtures\\Model\\JmsNested',
|
||||||
|
'required' => false,
|
||||||
|
'description' => '',
|
||||||
|
'readonly' => false,
|
||||||
|
'sinceVersion' => NULL,
|
||||||
|
'untilVersion' => NULL,
|
||||||
|
),
|
||||||
|
'nested_array' =>
|
||||||
|
array(
|
||||||
|
'dataType' => 'array of objects (JmsNested)',
|
||||||
|
'actualType' => 'collection',
|
||||||
|
'subType' => 'Nelmio\\ApiDocBundle\\Tests\\Fixtures\\Model\\JmsNested',
|
||||||
|
'required' => false,
|
||||||
|
'description' => '',
|
||||||
|
'readonly' => false,
|
||||||
|
'sinceVersion' => NULL,
|
||||||
|
'untilVersion' => NULL,
|
||||||
|
'default' => null,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
'since' =>
|
||||||
|
array(
|
||||||
|
'dataType' => 'string',
|
||||||
|
'actualType' => 'string',
|
||||||
|
'subType' => NULL,
|
||||||
|
'required' => false,
|
||||||
|
'description' => '',
|
||||||
|
'readonly' => false,
|
||||||
|
'sinceVersion' => '0.2',
|
||||||
|
'untilVersion' => NULL,
|
||||||
|
'default' => null,
|
||||||
|
),
|
||||||
|
'until' =>
|
||||||
|
array(
|
||||||
|
'dataType' => 'string',
|
||||||
|
'actualType' => 'string',
|
||||||
|
'subType' => NULL,
|
||||||
|
'required' => false,
|
||||||
|
'description' => '',
|
||||||
|
'readonly' => false,
|
||||||
|
'sinceVersion' => NULL,
|
||||||
|
'untilVersion' => '0.3',
|
||||||
|
'default' => null,
|
||||||
|
),
|
||||||
|
'since_and_until' =>
|
||||||
|
array(
|
||||||
|
'dataType' => 'string',
|
||||||
|
'actualType' => 'string',
|
||||||
|
'subType' => NULL,
|
||||||
|
'required' => false,
|
||||||
|
'description' => '',
|
||||||
|
'readonly' => false,
|
||||||
|
'sinceVersion' => '0.4',
|
||||||
|
'untilVersion' => '0.5',
|
||||||
|
'default' => null,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
'parent' =>
|
||||||
|
array(
|
||||||
|
'dataType' => 'object (JmsTest)',
|
||||||
|
'actualType' => 'model',
|
||||||
|
'default' => null,
|
||||||
|
'subType' => 'Nelmio\\ApiDocBundle\\Tests\\Fixtures\\Model\\JmsTest',
|
||||||
|
'required' => false,
|
||||||
|
'description' => '',
|
||||||
|
'readonly' => false,
|
||||||
|
'sinceVersion' => NULL,
|
||||||
|
'untilVersion' => NULL,
|
||||||
|
'children' =>
|
||||||
|
array(
|
||||||
|
'foo' =>
|
||||||
|
array(
|
||||||
|
'dataType' => 'string',
|
||||||
|
'actualType' => 'string',
|
||||||
|
'subType' => NULL,
|
||||||
|
'default' => null,
|
||||||
|
'required' => false,
|
||||||
|
'description' => '',
|
||||||
|
'readonly' => false,
|
||||||
|
'sinceVersion' => NULL,
|
||||||
|
'untilVersion' => NULL,
|
||||||
|
),
|
||||||
|
'bar' =>
|
||||||
|
array(
|
||||||
|
'dataType' => 'DateTime',
|
||||||
|
'actualType' => 'datetime',
|
||||||
|
'subType' => NULL,
|
||||||
|
'required' => false,
|
||||||
|
'description' => '',
|
||||||
|
'readonly' => true,
|
||||||
|
'sinceVersion' => NULL,
|
||||||
|
'untilVersion' => NULL,
|
||||||
|
'default' => null,
|
||||||
|
),
|
||||||
|
'number' =>
|
||||||
|
array(
|
||||||
|
'dataType' => 'double',
|
||||||
|
'actualType' => 'float',
|
||||||
|
'subType' => NULL,
|
||||||
|
'required' => false,
|
||||||
|
'description' => '',
|
||||||
|
'readonly' => false,
|
||||||
|
'sinceVersion' => NULL,
|
||||||
|
'untilVersion' => NULL,
|
||||||
|
'default' => null,
|
||||||
|
),
|
||||||
|
'arr' =>
|
||||||
|
array(
|
||||||
|
'dataType' => 'array',
|
||||||
|
'actualType' => 'collection',
|
||||||
|
'subType' => NULL,
|
||||||
|
'required' => false,
|
||||||
|
'description' => '',
|
||||||
|
'readonly' => false,
|
||||||
|
'sinceVersion' => NULL,
|
||||||
|
'untilVersion' => NULL,
|
||||||
|
'default' => null,
|
||||||
|
),
|
||||||
|
'nested' =>
|
||||||
|
array(
|
||||||
|
'dataType' => 'object (JmsNested)',
|
||||||
|
'actualType' => 'model',
|
||||||
|
'default' => null,
|
||||||
|
'subType' => 'Nelmio\\ApiDocBundle\\Tests\\Fixtures\\Model\\JmsNested',
|
||||||
|
'required' => false,
|
||||||
|
'description' => '',
|
||||||
|
'readonly' => false,
|
||||||
|
'sinceVersion' => NULL,
|
||||||
|
'untilVersion' => NULL,
|
||||||
|
),
|
||||||
|
'nested_array' =>
|
||||||
|
array(
|
||||||
|
'dataType' => 'array of objects (JmsNested)',
|
||||||
|
'actualType' => 'collection',
|
||||||
|
'subType' => 'Nelmio\\ApiDocBundle\\Tests\\Fixtures\\Model\\JmsNested',
|
||||||
|
'required' => false,
|
||||||
|
'description' => '',
|
||||||
|
'readonly' => false,
|
||||||
|
'sinceVersion' => NULL,
|
||||||
|
'untilVersion' => NULL,
|
||||||
|
'default' => null,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
'since' =>
|
||||||
|
array(
|
||||||
|
'dataType' => 'string',
|
||||||
|
'actualType' => 'string',
|
||||||
|
'subType' => NULL,
|
||||||
|
'required' => false,
|
||||||
|
'description' => '',
|
||||||
|
'readonly' => false,
|
||||||
|
'sinceVersion' => '0.2',
|
||||||
|
'untilVersion' => NULL,
|
||||||
|
'default' => null,
|
||||||
|
),
|
||||||
|
'until' =>
|
||||||
|
array(
|
||||||
|
'dataType' => 'string',
|
||||||
|
'actualType' => 'string',
|
||||||
|
'subType' => NULL,
|
||||||
|
'required' => false,
|
||||||
|
'description' => '',
|
||||||
|
'readonly' => false,
|
||||||
|
'sinceVersion' => NULL,
|
||||||
|
'untilVersion' => '0.3',
|
||||||
|
'default' => null,
|
||||||
|
),
|
||||||
|
'since_and_until' =>
|
||||||
|
array(
|
||||||
|
'dataType' => 'string',
|
||||||
|
'actualType' => 'string',
|
||||||
|
'subType' => NULL,
|
||||||
|
'required' => false,
|
||||||
|
'description' => '',
|
||||||
|
'readonly' => false,
|
||||||
|
'sinceVersion' => '0.4',
|
||||||
|
'untilVersion' => '0.5',
|
||||||
|
'default' => null,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
'https' => false,
|
||||||
|
'authentication' => false,
|
||||||
|
'authenticationRoles' =>
|
||||||
|
array(),
|
||||||
|
'deprecated' => false,
|
||||||
|
),
|
||||||
|
array(
|
||||||
|
'method' => 'GET',
|
||||||
|
'uri' => '/api/resources/{id}.{_format}',
|
||||||
|
'description' => 'Retrieve a resource by ID.',
|
||||||
|
'requirements' =>
|
||||||
|
array(
|
||||||
|
'_format' =>
|
||||||
|
array(
|
||||||
|
'requirement' => 'json|xml|html',
|
||||||
|
'dataType' => '',
|
||||||
|
'description' => '',
|
||||||
|
),
|
||||||
|
'id' =>
|
||||||
|
array(
|
||||||
|
'requirement' => '',
|
||||||
|
'dataType' => '',
|
||||||
|
'description' => '',
|
||||||
|
),
|
||||||
|
),
|
||||||
|
'https' => false,
|
||||||
|
'authentication' => false,
|
||||||
|
'authenticationRoles' =>
|
||||||
|
array(),
|
||||||
|
'deprecated' => false,
|
||||||
|
),
|
||||||
|
array(
|
||||||
|
'method' => 'DELETE',
|
||||||
|
'uri' => '/api/resources/{id}.{_format}',
|
||||||
|
'description' => 'Delete a resource by ID.',
|
||||||
|
'requirements' =>
|
||||||
|
array(
|
||||||
|
'_format' =>
|
||||||
|
array(
|
||||||
|
'requirement' => 'json|xml|html',
|
||||||
|
'dataType' => '',
|
||||||
|
'description' => '',
|
||||||
|
),
|
||||||
|
'id' =>
|
||||||
|
array(
|
||||||
|
'requirement' => '',
|
||||||
|
'dataType' => '',
|
||||||
|
'description' => '',
|
||||||
|
),
|
||||||
|
),
|
||||||
|
'https' => false,
|
||||||
|
'authentication' => false,
|
||||||
|
'authenticationRoles' =>
|
||||||
|
array(),
|
||||||
|
'deprecated' => false,
|
||||||
|
),
|
||||||
|
),
|
||||||
);
|
);
|
||||||
|
|
||||||
$this->assertEquals($expected, $result);
|
$this->assertEquals($expected, $result);
|
||||||
|
730
Tests/Formatter/SwaggerFormatterTest.php
Normal file
730
Tests/Formatter/SwaggerFormatterTest.php
Normal file
@ -0,0 +1,730 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Nelmio\ApiDocBundle\Tests\Formatter;
|
||||||
|
|
||||||
|
|
||||||
|
use Nelmio\ApiDocBundle\Extractor\ApiDocExtractor;
|
||||||
|
use Nelmio\ApiDocBundle\Formatter\SwaggerFormatter;
|
||||||
|
use Nelmio\ApiDocBundle\Tests\WebTestCase;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Class SwaggerFormatterTest
|
||||||
|
*
|
||||||
|
* @package Nelmio\ApiDocBundle\Tests\Formatter
|
||||||
|
* @author Bez Hermoso <bez@activelamp.com>
|
||||||
|
*/
|
||||||
|
class SwaggerFormatterTest extends WebTestCase
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @var ApiDocExtractor
|
||||||
|
*/
|
||||||
|
protected $extractor;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var SwaggerFormatter
|
||||||
|
*/
|
||||||
|
protected $formatter;
|
||||||
|
|
||||||
|
protected function setUp()
|
||||||
|
{
|
||||||
|
parent::setUp();
|
||||||
|
|
||||||
|
$container = $this->getContainer();
|
||||||
|
$this->extractor = $container->get('nelmio_api_doc.extractor.api_doc_extractor');
|
||||||
|
$this->formatter = $container->get('nelmio_api_doc.formatter.swagger_formatter');
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public function testResourceListing()
|
||||||
|
{
|
||||||
|
|
||||||
|
set_error_handler(array($this, 'handleDeprecation'));
|
||||||
|
$data = $this->extractor->all();
|
||||||
|
restore_error_handler();
|
||||||
|
|
||||||
|
/** @var $formatter SwaggerFormatter */
|
||||||
|
|
||||||
|
$actual = $this->formatter->format($data, null);
|
||||||
|
|
||||||
|
|
||||||
|
$expected = array(
|
||||||
|
'swaggerVersion' => '1.2',
|
||||||
|
'apiVersion' => '3.14',
|
||||||
|
'info' =>
|
||||||
|
array(
|
||||||
|
'title' => 'Nelmio Swagger',
|
||||||
|
'description' => 'Testing Swagger integration.',
|
||||||
|
'TermsOfServiceUrl' => 'https://github.com',
|
||||||
|
'contact' => 'user@domain.tld',
|
||||||
|
'license' => 'MIT',
|
||||||
|
'licenseUrl' => 'http://opensource.org/licenses/MIT',
|
||||||
|
),
|
||||||
|
'authorizations' =>
|
||||||
|
array(),
|
||||||
|
'apis' =>
|
||||||
|
array(
|
||||||
|
array(
|
||||||
|
'path' => '/other-resources',
|
||||||
|
'description' => 'Operations on another resource.',
|
||||||
|
),
|
||||||
|
array(
|
||||||
|
'path' => '/resources',
|
||||||
|
'description' => 'Operations on resource.',
|
||||||
|
),
|
||||||
|
array(
|
||||||
|
'path' => '/tests',
|
||||||
|
'description' => NULL,
|
||||||
|
),
|
||||||
|
array(
|
||||||
|
'path' => '/tests',
|
||||||
|
'description' => NULL,
|
||||||
|
),
|
||||||
|
array(
|
||||||
|
'path' => '/tests2',
|
||||||
|
'description' => NULL,
|
||||||
|
),
|
||||||
|
array(
|
||||||
|
'path' => '/TestResource',
|
||||||
|
'description' => NULL,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
$this->assertEquals($expected, $actual);
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @dataProvider dataTestApiDeclaration
|
||||||
|
*/
|
||||||
|
public function testApiDeclaration($resource, $expected)
|
||||||
|
{
|
||||||
|
set_error_handler(array($this, 'handleDeprecation'));
|
||||||
|
$data = $this->extractor->all();
|
||||||
|
restore_error_handler();
|
||||||
|
|
||||||
|
$actual = $this->formatter->format($data, $resource);
|
||||||
|
|
||||||
|
$this->assertEquals($expected, $actual);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public function dataTestApiDeclaration()
|
||||||
|
{
|
||||||
|
return array(
|
||||||
|
array(
|
||||||
|
'/resources',
|
||||||
|
array(
|
||||||
|
'swaggerVersion' => '1.2',
|
||||||
|
'apiVersion' => '3.14',
|
||||||
|
'basePath' => '/api',
|
||||||
|
'resourcePath' => '/resources',
|
||||||
|
'apis' =>
|
||||||
|
array(
|
||||||
|
|
||||||
|
array(
|
||||||
|
'path' => '/resources.{_format}',
|
||||||
|
'operations' =>
|
||||||
|
array(
|
||||||
|
array(
|
||||||
|
'method' => 'GET',
|
||||||
|
'summary' => 'List resources.',
|
||||||
|
'nickname' => 'get_resources',
|
||||||
|
'parameters' =>
|
||||||
|
array(
|
||||||
|
|
||||||
|
array(
|
||||||
|
'paramType' => 'path',
|
||||||
|
'name' => '_format',
|
||||||
|
'type' => 'string',
|
||||||
|
'required' => true,
|
||||||
|
'enum' =>
|
||||||
|
array(
|
||||||
|
'json',
|
||||||
|
'xml',
|
||||||
|
'html',
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
'responseMessages' =>
|
||||||
|
array(
|
||||||
|
|
||||||
|
array(
|
||||||
|
'code' => 200,
|
||||||
|
'message' => 'Returned on success.',
|
||||||
|
),
|
||||||
|
|
||||||
|
array(
|
||||||
|
'code' => 404,
|
||||||
|
'message' => 'Returned if resource cannot be found.',
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
|
||||||
|
array(
|
||||||
|
'method' => 'POST',
|
||||||
|
'summary' => 'Create a new resource.',
|
||||||
|
'nickname' => 'post_resources',
|
||||||
|
'parameters' =>
|
||||||
|
array(
|
||||||
|
|
||||||
|
array(
|
||||||
|
'paramType' => 'path',
|
||||||
|
'name' => '_format',
|
||||||
|
'type' => 'string',
|
||||||
|
'required' => true,
|
||||||
|
'enum' =>
|
||||||
|
array(
|
||||||
|
'json',
|
||||||
|
'xml',
|
||||||
|
'html',
|
||||||
|
),
|
||||||
|
),
|
||||||
|
|
||||||
|
array(
|
||||||
|
'paramType' => 'form',
|
||||||
|
'name' => 'a',
|
||||||
|
'type' => 'string',
|
||||||
|
),
|
||||||
|
|
||||||
|
array(
|
||||||
|
'paramType' => 'form',
|
||||||
|
'name' => 'b',
|
||||||
|
'type' => 'number',
|
||||||
|
'format' => 'float',
|
||||||
|
),
|
||||||
|
|
||||||
|
array(
|
||||||
|
'paramType' => 'form',
|
||||||
|
'name' => 'c',
|
||||||
|
'type' => 'string',
|
||||||
|
'enum' =>
|
||||||
|
array(
|
||||||
|
'x',
|
||||||
|
'y',
|
||||||
|
'z',
|
||||||
|
),
|
||||||
|
),
|
||||||
|
|
||||||
|
array(
|
||||||
|
'paramType' => 'form',
|
||||||
|
'name' => 'd',
|
||||||
|
'type' => 'string',
|
||||||
|
'format' => 'date-time',
|
||||||
|
),
|
||||||
|
|
||||||
|
array(
|
||||||
|
'paramType' => 'form',
|
||||||
|
'name' => 'e',
|
||||||
|
'type' => 'string',
|
||||||
|
'format' => 'date',
|
||||||
|
),
|
||||||
|
|
||||||
|
array(
|
||||||
|
'paramType' => 'form',
|
||||||
|
'name' => 'g',
|
||||||
|
'type' => 'string',
|
||||||
|
),
|
||||||
|
),
|
||||||
|
'responseMessages' =>
|
||||||
|
array(
|
||||||
|
array(
|
||||||
|
'code' => 200,
|
||||||
|
'message' => 'See standard HTTP status code reason for 200',
|
||||||
|
'responseModel' => 'Nelmio.ApiDocBundle.Tests.Fixtures.Model.JmsNested',
|
||||||
|
),
|
||||||
|
),
|
||||||
|
'type' => 'Nelmio.ApiDocBundle.Tests.Fixtures.Model.JmsNested',
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
|
||||||
|
array(
|
||||||
|
'path' => '/resources/{id}.{_format}',
|
||||||
|
'operations' =>
|
||||||
|
array(
|
||||||
|
|
||||||
|
array(
|
||||||
|
'method' => 'GET',
|
||||||
|
'summary' => 'Retrieve a resource by ID.',
|
||||||
|
'nickname' => 'get_resources',
|
||||||
|
'parameters' =>
|
||||||
|
array(
|
||||||
|
|
||||||
|
array(
|
||||||
|
'paramType' => 'path',
|
||||||
|
'name' => 'id',
|
||||||
|
'type' => 'string',
|
||||||
|
'required' => true,
|
||||||
|
),
|
||||||
|
|
||||||
|
array(
|
||||||
|
'paramType' => 'path',
|
||||||
|
'name' => '_format',
|
||||||
|
'type' => 'string',
|
||||||
|
'required' => true,
|
||||||
|
'enum' =>
|
||||||
|
array(
|
||||||
|
'json',
|
||||||
|
'xml',
|
||||||
|
'html',
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
'responseMessages' =>
|
||||||
|
array(),
|
||||||
|
),
|
||||||
|
|
||||||
|
array(
|
||||||
|
'method' => 'DELETE',
|
||||||
|
'summary' => 'Delete a resource by ID.',
|
||||||
|
'nickname' => 'delete_resources',
|
||||||
|
'parameters' =>
|
||||||
|
array(
|
||||||
|
|
||||||
|
array(
|
||||||
|
'paramType' => 'path',
|
||||||
|
'name' => 'id',
|
||||||
|
'type' => 'string',
|
||||||
|
'required' => true,
|
||||||
|
),
|
||||||
|
|
||||||
|
array(
|
||||||
|
'paramType' => 'path',
|
||||||
|
'name' => '_format',
|
||||||
|
'type' => 'string',
|
||||||
|
'required' => true,
|
||||||
|
'enum' =>
|
||||||
|
array(
|
||||||
|
'json',
|
||||||
|
'xml',
|
||||||
|
'html',
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
'responseMessages' =>
|
||||||
|
array(),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
'models' =>
|
||||||
|
array(
|
||||||
|
'Nelmio.ApiDocBundle.Tests.Fixtures.Model.JmsTest' =>
|
||||||
|
array(
|
||||||
|
'id' => 'Nelmio.ApiDocBundle.Tests.Fixtures.Model.JmsTest',
|
||||||
|
'description' => 'object (JmsTest)',
|
||||||
|
'properties' =>
|
||||||
|
array(
|
||||||
|
'foo' =>
|
||||||
|
array(
|
||||||
|
'type' => 'string',
|
||||||
|
'description' => 'string',
|
||||||
|
),
|
||||||
|
'bar' =>
|
||||||
|
array(
|
||||||
|
'type' => 'string',
|
||||||
|
'description' => 'DateTime',
|
||||||
|
'format' => 'date-time',
|
||||||
|
),
|
||||||
|
'number' =>
|
||||||
|
array(
|
||||||
|
'type' => 'number',
|
||||||
|
'description' => 'double',
|
||||||
|
'format' => 'float',
|
||||||
|
),
|
||||||
|
'arr' =>
|
||||||
|
array(
|
||||||
|
'type' => 'array',
|
||||||
|
'description' => 'array',
|
||||||
|
'items' => array(
|
||||||
|
'type' => 'string',
|
||||||
|
)
|
||||||
|
),
|
||||||
|
'nested' =>
|
||||||
|
array(
|
||||||
|
'$ref' => 'Nelmio.ApiDocBundle.Tests.Fixtures.Model.JmsNested',
|
||||||
|
),
|
||||||
|
'nested_array' =>
|
||||||
|
array(
|
||||||
|
'type' => 'array',
|
||||||
|
'description' => 'array of objects (JmsNested)',
|
||||||
|
'items' => array(
|
||||||
|
'$ref' => 'Nelmio.ApiDocBundle.Tests.Fixtures.Model.JmsNested',
|
||||||
|
)
|
||||||
|
),
|
||||||
|
),
|
||||||
|
'required' =>
|
||||||
|
array(),
|
||||||
|
),
|
||||||
|
'Nelmio.ApiDocBundle.Tests.Fixtures.Model.JmsNested' =>
|
||||||
|
array(
|
||||||
|
'id' => 'Nelmio.ApiDocBundle.Tests.Fixtures.Model.JmsNested',
|
||||||
|
'description' => '',
|
||||||
|
'properties' =>
|
||||||
|
array(
|
||||||
|
'foo' =>
|
||||||
|
array(
|
||||||
|
'type' => 'string',
|
||||||
|
'description' => 'DateTime',
|
||||||
|
'format' => 'date-time',
|
||||||
|
),
|
||||||
|
'bar' =>
|
||||||
|
array(
|
||||||
|
'type' => 'string',
|
||||||
|
'description' => 'string',
|
||||||
|
),
|
||||||
|
'baz' =>
|
||||||
|
array(
|
||||||
|
'type' => 'array',
|
||||||
|
'description' => 'Epic description.
|
||||||
|
|
||||||
|
With multiple lines.',
|
||||||
|
'items' => array(
|
||||||
|
'type' => 'string',
|
||||||
|
)
|
||||||
|
),
|
||||||
|
'circular' =>
|
||||||
|
array(
|
||||||
|
'$ref' => 'Nelmio.ApiDocBundle.Tests.Fixtures.Model.JmsNested',
|
||||||
|
),
|
||||||
|
'parent' =>
|
||||||
|
array(
|
||||||
|
'$ref' => 'Nelmio.ApiDocBundle.Tests.Fixtures.Model.JmsTest',
|
||||||
|
),
|
||||||
|
'since' =>
|
||||||
|
array(
|
||||||
|
'type' => 'string',
|
||||||
|
'description' => 'string',
|
||||||
|
),
|
||||||
|
'until' =>
|
||||||
|
array(
|
||||||
|
'type' => 'string',
|
||||||
|
'description' => 'string',
|
||||||
|
),
|
||||||
|
'since_and_until' =>
|
||||||
|
array(
|
||||||
|
'type' => 'string',
|
||||||
|
'description' => 'string',
|
||||||
|
),
|
||||||
|
),
|
||||||
|
'required' =>
|
||||||
|
array(),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
'produces' =>
|
||||||
|
array(),
|
||||||
|
'consumes' =>
|
||||||
|
array(),
|
||||||
|
'authorizations' =>
|
||||||
|
array(),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
array(
|
||||||
|
'/other-resources',
|
||||||
|
array(
|
||||||
|
'swaggerVersion' => '1.2',
|
||||||
|
'apiVersion' => '3.14',
|
||||||
|
'basePath' => '/api',
|
||||||
|
'resourcePath' => '/other-resources',
|
||||||
|
'apis' =>
|
||||||
|
array(
|
||||||
|
|
||||||
|
array(
|
||||||
|
'path' => '/other-resources.{_format}',
|
||||||
|
'operations' =>
|
||||||
|
array(
|
||||||
|
|
||||||
|
array(
|
||||||
|
'method' => 'GET',
|
||||||
|
'summary' => 'List another resource.',
|
||||||
|
'nickname' => 'get_other-resources',
|
||||||
|
'parameters' =>
|
||||||
|
array(
|
||||||
|
|
||||||
|
array(
|
||||||
|
'paramType' => 'path',
|
||||||
|
'name' => '_format',
|
||||||
|
'type' => 'string',
|
||||||
|
'required' => true,
|
||||||
|
'enum' =>
|
||||||
|
array(
|
||||||
|
'json',
|
||||||
|
'xml',
|
||||||
|
'html',
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
'responseMessages' =>
|
||||||
|
array(),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
|
||||||
|
array(
|
||||||
|
'path' => '/other-resources/{id}.{_format}',
|
||||||
|
'operations' =>
|
||||||
|
array(
|
||||||
|
|
||||||
|
array(
|
||||||
|
'method' => 'PUT',
|
||||||
|
'summary' => 'Update a resource bu ID.',
|
||||||
|
'nickname' => 'put_other-resources',
|
||||||
|
'parameters' =>
|
||||||
|
array(
|
||||||
|
|
||||||
|
array(
|
||||||
|
'paramType' => 'path',
|
||||||
|
'name' => 'id',
|
||||||
|
'type' => 'string',
|
||||||
|
'required' => true,
|
||||||
|
),
|
||||||
|
|
||||||
|
array(
|
||||||
|
'paramType' => 'path',
|
||||||
|
'name' => '_format',
|
||||||
|
'type' => 'string',
|
||||||
|
'required' => true,
|
||||||
|
'enum' =>
|
||||||
|
array(
|
||||||
|
'json',
|
||||||
|
'xml',
|
||||||
|
'html',
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
'responseMessages' =>
|
||||||
|
array(),
|
||||||
|
),
|
||||||
|
|
||||||
|
array(
|
||||||
|
'method' => 'PATCH',
|
||||||
|
'summary' => 'Update a resource bu ID.',
|
||||||
|
'nickname' => 'patch_other-resources',
|
||||||
|
'parameters' =>
|
||||||
|
array(
|
||||||
|
|
||||||
|
array(
|
||||||
|
'paramType' => 'path',
|
||||||
|
'name' => 'id',
|
||||||
|
'type' => 'string',
|
||||||
|
'required' => true,
|
||||||
|
),
|
||||||
|
|
||||||
|
array(
|
||||||
|
'paramType' => 'path',
|
||||||
|
'name' => '_format',
|
||||||
|
'type' => 'string',
|
||||||
|
'required' => true,
|
||||||
|
'enum' =>
|
||||||
|
array(
|
||||||
|
'json',
|
||||||
|
'xml',
|
||||||
|
'html',
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
'responseMessages' =>
|
||||||
|
array(),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
'models' =>
|
||||||
|
array(),
|
||||||
|
'produces' =>
|
||||||
|
array(),
|
||||||
|
'consumes' =>
|
||||||
|
array(),
|
||||||
|
'authorizations' =>
|
||||||
|
array(),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
array(
|
||||||
|
'/tests',
|
||||||
|
array (
|
||||||
|
'swaggerVersion' => '1.2',
|
||||||
|
'apiVersion' => '3.14',
|
||||||
|
'basePath' => '/api',
|
||||||
|
'resourcePath' => '/tests',
|
||||||
|
'apis' =>
|
||||||
|
array (
|
||||||
|
|
||||||
|
array (
|
||||||
|
'path' => '/tests.{_format}',
|
||||||
|
'operations' =>
|
||||||
|
array (
|
||||||
|
|
||||||
|
array (
|
||||||
|
'method' => 'GET',
|
||||||
|
'summary' => 'index action',
|
||||||
|
'nickname' => 'get_tests',
|
||||||
|
'parameters' =>
|
||||||
|
array (
|
||||||
|
|
||||||
|
array (
|
||||||
|
'paramType' => 'path',
|
||||||
|
'name' => '_format',
|
||||||
|
'type' => 'string',
|
||||||
|
'required' => true,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
'responseMessages' =>
|
||||||
|
array (
|
||||||
|
),
|
||||||
|
),
|
||||||
|
|
||||||
|
array (
|
||||||
|
'method' => 'GET',
|
||||||
|
'summary' => 'index action',
|
||||||
|
'nickname' => 'get_tests',
|
||||||
|
'parameters' =>
|
||||||
|
array (
|
||||||
|
|
||||||
|
array (
|
||||||
|
'paramType' => 'path',
|
||||||
|
'name' => '_format',
|
||||||
|
'type' => 'string',
|
||||||
|
'required' => true,
|
||||||
|
),
|
||||||
|
|
||||||
|
array (
|
||||||
|
'paramType' => 'query',
|
||||||
|
'name' => 'a',
|
||||||
|
'type' => 'integer',
|
||||||
|
),
|
||||||
|
|
||||||
|
array (
|
||||||
|
'paramType' => 'query',
|
||||||
|
'name' => 'b',
|
||||||
|
'type' => 'string',
|
||||||
|
),
|
||||||
|
),
|
||||||
|
'responseMessages' =>
|
||||||
|
array (
|
||||||
|
),
|
||||||
|
),
|
||||||
|
|
||||||
|
array (
|
||||||
|
'method' => 'POST',
|
||||||
|
'summary' => 'create test',
|
||||||
|
'nickname' => 'post_tests',
|
||||||
|
'parameters' =>
|
||||||
|
array (
|
||||||
|
|
||||||
|
array (
|
||||||
|
'paramType' => 'path',
|
||||||
|
'name' => '_format',
|
||||||
|
'type' => 'string',
|
||||||
|
'required' => true,
|
||||||
|
),
|
||||||
|
|
||||||
|
array (
|
||||||
|
'paramType' => 'query',
|
||||||
|
'name' => 'a',
|
||||||
|
'type' => 'integer',
|
||||||
|
),
|
||||||
|
|
||||||
|
array (
|
||||||
|
'paramType' => 'query',
|
||||||
|
'name' => 'b',
|
||||||
|
'type' => 'string',
|
||||||
|
),
|
||||||
|
|
||||||
|
array (
|
||||||
|
'paramType' => 'form',
|
||||||
|
'name' => 'a',
|
||||||
|
'type' => 'string',
|
||||||
|
),
|
||||||
|
|
||||||
|
array (
|
||||||
|
'paramType' => 'form',
|
||||||
|
'name' => 'b',
|
||||||
|
'type' => 'string',
|
||||||
|
),
|
||||||
|
|
||||||
|
array (
|
||||||
|
'paramType' => 'form',
|
||||||
|
'name' => 'c',
|
||||||
|
'type' => 'boolean',
|
||||||
|
'defaultValue' => false,
|
||||||
|
),
|
||||||
|
|
||||||
|
array (
|
||||||
|
'paramType' => 'form',
|
||||||
|
'name' => 'd',
|
||||||
|
'type' => 'string',
|
||||||
|
'defaultValue' => 'DefaultTest',
|
||||||
|
),
|
||||||
|
),
|
||||||
|
'responseMessages' =>
|
||||||
|
array (
|
||||||
|
),
|
||||||
|
),
|
||||||
|
|
||||||
|
array (
|
||||||
|
'method' => 'POST',
|
||||||
|
'summary' => 'create test',
|
||||||
|
'nickname' => 'post_tests',
|
||||||
|
'parameters' =>
|
||||||
|
array (
|
||||||
|
|
||||||
|
array (
|
||||||
|
'paramType' => 'path',
|
||||||
|
'name' => '_format',
|
||||||
|
'type' => 'string',
|
||||||
|
'required' => true,
|
||||||
|
),
|
||||||
|
|
||||||
|
array (
|
||||||
|
'paramType' => 'form',
|
||||||
|
'name' => 'a',
|
||||||
|
'type' => 'string',
|
||||||
|
),
|
||||||
|
|
||||||
|
array (
|
||||||
|
'paramType' => 'form',
|
||||||
|
'name' => 'b',
|
||||||
|
'type' => 'string',
|
||||||
|
),
|
||||||
|
|
||||||
|
array (
|
||||||
|
'paramType' => 'form',
|
||||||
|
'name' => 'c',
|
||||||
|
'type' => 'boolean',
|
||||||
|
'defaultValue' => false,
|
||||||
|
),
|
||||||
|
|
||||||
|
array (
|
||||||
|
'paramType' => 'form',
|
||||||
|
'name' => 'd',
|
||||||
|
'type' => 'string',
|
||||||
|
'defaultValue' => 'DefaultTest',
|
||||||
|
),
|
||||||
|
),
|
||||||
|
'responseMessages' =>
|
||||||
|
array (
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
'models' =>
|
||||||
|
array (
|
||||||
|
),
|
||||||
|
'produces' =>
|
||||||
|
array (
|
||||||
|
),
|
||||||
|
'consumes' =>
|
||||||
|
array (
|
||||||
|
),
|
||||||
|
'authorizations' =>
|
||||||
|
array (
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user