Refactoring

Move logic to extract data in the Extractor
Remove logic in the AbstractFormatter
Use ApiDoc class as data container
Update tests
Add test to prove the bug with FOSRestBundle annotations (\\d+ instead of \d+)
This commit is contained in:
William DURAND 2012-07-20 00:58:58 +02:00
parent 8f3327b376
commit cca97cf6af
14 changed files with 371 additions and 256 deletions

View File

@ -11,6 +11,8 @@
namespace Nelmio\ApiDocBundle\Annotation; namespace Nelmio\ApiDocBundle\Annotation;
use Symfony\Component\Routing\Route;
/** /**
* @Annotation * @Annotation
*/ */
@ -41,6 +43,31 @@ class ApiDoc
*/ */
private $isResource = false; private $isResource = false;
/**
* @var array
*/
private $requirements = array();
/**
* @var string
*/
private $method;
/**
* @var string
*/
private $uri;
/**
* @var array
*/
private $parameters = array();
/**
* @var Route
*/
private $route;
public function __construct(array $data) public function __construct(array $data)
{ {
if (isset($data['formType'])) { if (isset($data['formType'])) {
@ -65,14 +92,6 @@ class ApiDoc
$this->isResource = isset($data['resource']) && $data['resource']; $this->isResource = isset($data['resource']) && $data['resource'];
} }
/**
* @return array
*/
public function getFilters()
{
return $this->filters;
}
/** /**
* @param string $name * @param string $name
* @param array $filter * @param array $filter
@ -82,6 +101,23 @@ class ApiDoc
$this->filters[$name] = $filter; $this->filters[$name] = $filter;
} }
/**
* @param string $name
* @param array $requirement
*/
public function addRequirement($name, array $requirement)
{
$this->requirements[$name] = $requirement;
}
/**
* @param array $requirements
*/
public function setRequirements(array $requirements)
{
$this->requirements = array_merge($this->requirements, $requirements);
}
/** /**
* @return string|null * @return string|null
*/ */
@ -91,7 +127,7 @@ class ApiDoc
} }
/** /**
* @return string|null * @return string
*/ */
public function getDescription() public function getDescription()
{ {
@ -106,14 +142,6 @@ class ApiDoc
$this->description = $description; $this->description = $description;
} }
/**
* @return string|null
*/
public function getDocumentation()
{
return $this->documentation;
}
/** /**
* @param string $documentation * @param string $documentation
*/ */
@ -129,4 +157,77 @@ class ApiDoc
{ {
return $this->isResource; return $this->isResource;
} }
/**
* @var string $method
*/
public function setMethod($method)
{
$this->method = $method;
}
/**
* @var string $uri
*/
public function setUri($uri)
{
$this->uri = $uri;
}
/**
* @param array $parameters
*/
public function setParameters(array $parameters)
{
$this->parameters = $parameters;
}
/**
* @param Route $route
*/
public function setRoute(Route $route)
{
$this->route = $route;
}
/**
* @return Route
*/
public function getRoute()
{
return $this->route;
}
/**
* @return array
*/
public function toArray()
{
$data = array(
'method' => $this->method,
'uri' => $this->uri,
);
if ($description = $this->description) {
$data['description'] = $description;
}
if ($documentation = $this->documentation) {
$data['documentation'] = $documentation;
}
if ($filters = $this->filters) {
$data['filters'] = $filters;
}
if ($parameters = $this->parameters) {
$data['parameters'] = $parameters;
}
if ($requirements = $this->requirements) {
$data['requirements'] = $requirements;
}
return $data;
}
} }

View File

@ -54,8 +54,8 @@ class RequestListener
$controller = $request->attributes->get('_controller'); $controller = $request->attributes->get('_controller');
$route = $request->attributes->get('_route'); $route = $request->attributes->get('_route');
if (null !== $array = $this->extractor->get($controller, $route)) { if (null !== $annotation = $this->extractor->get($controller, $route)) {
$result = $this->formatter->formatOne($array['annotation'], $array['route']); $result = $this->formatter->formatOne($annotation);
$event->setResponse(new Response($result, 200, array( $event->setResponse(new Response($result, 200, array(
'Content-Type' => 'text/html' 'Content-Type' => 'text/html'

View File

@ -13,6 +13,7 @@ namespace Nelmio\ApiDocBundle\Extractor;
use Doctrine\Common\Annotations\Reader; use Doctrine\Common\Annotations\Reader;
use Nelmio\ApiDocBundle\Annotation\ApiDoc; use Nelmio\ApiDocBundle\Annotation\ApiDoc;
use Nelmio\ApiDocBundle\Parser\FormTypeParser;
use Symfony\Component\Routing\Route; use Symfony\Component\Routing\Route;
use Symfony\Component\Routing\RouterInterface; use Symfony\Component\Routing\RouterInterface;
use Symfony\Component\DependencyInjection\ContainerInterface; use Symfony\Component\DependencyInjection\ContainerInterface;
@ -39,17 +40,22 @@ class ApiDocExtractor
*/ */
private $reader; private $reader;
public function __construct(ContainerInterface $container, RouterInterface $router, Reader $reader) /**
* @var \Nelmio\ApiDocBundle\Parser\FormTypeParser
*/
private $parser;
public function __construct(ContainerInterface $container, RouterInterface $router, Reader $reader, FormTypeParser $parser)
{ {
$this->container = $container; $this->container = $container;
$this->router = $router; $this->router = $router;
$this->reader = $reader; $this->reader = $reader;
$this->parser = $parser;
} }
/** /**
* Returns an array of data where each data is an array with the following keys: * Returns an array of data where each data is an array with the following keys:
* - annotation * - annotation
* - route
* - resource * - resource
* *
* @return array * @return array
@ -61,12 +67,12 @@ class ApiDocExtractor
foreach ($this->router->getRouteCollection()->all() as $route) { foreach ($this->router->getRouteCollection()->all() as $route) {
if ($method = $this->getReflectionMethod($route->getDefault('_controller'))) { if ($method = $this->getReflectionMethod($route->getDefault('_controller'))) {
if ($annot = $this->reader->getMethodAnnotation($method, self::ANNOTATION_CLASS)) { if ($annotation = $this->reader->getMethodAnnotation($method, self::ANNOTATION_CLASS)) {
if ($annot->isResource()) { if ($annotation->isResource()) {
$resources[] = $route->getPattern(); $resources[] = $route->getPattern();
} }
$array[] = $this->parseAnnotations($annot, $method, $route); $array[] = array('annotation' => $this->extractData($annotation, $route, $method));
} }
} }
} }
@ -74,7 +80,7 @@ class ApiDocExtractor
rsort($resources); rsort($resources);
foreach ($array as $index => $element) { foreach ($array as $index => $element) {
$hasResource = false; $hasResource = false;
$pattern = $element['route']->getPattern(); $pattern = $element['annotation']->getRoute()->getPattern();
foreach ($resources as $resource) { foreach ($resources as $resource) {
if (0 === strpos($pattern, $resource)) { if (0 === strpos($pattern, $resource)) {
@ -91,31 +97,38 @@ class ApiDocExtractor
} }
$methodOrder = array('GET', 'POST', 'PUT', 'DELETE'); $methodOrder = array('GET', 'POST', 'PUT', 'DELETE');
usort($array, function($a, $b) use ($methodOrder) { usort($array, function($a, $b) use ($methodOrder) {
if ($a['resource'] === $b['resource']) { if ($a['resource'] === $b['resource']) {
if ($a['route']->getPattern() === $b['route']->getPattern()) { if ($a['annotation']->getRoute()->getPattern() === $b['annotation']->getRoute()->getPattern()) {
$methodA = array_search($a['route']->getRequirement('_method'), $methodOrder); $methodA = array_search($a['annotation']->getRoute()->getRequirement('_method'), $methodOrder);
$methodB = array_search($b['route']->getRequirement('_method'), $methodOrder); $methodB = array_search($b['annotation']->getRoute()->getRequirement('_method'), $methodOrder);
if ($methodA === $methodB) { if ($methodA === $methodB) {
return strcmp($a['route']->getRequirement('_method'), $b['route']->getRequirement('_method')); return strcmp(
$a['annotation']->getRoute()->getRequirement('_method'),
$b['annotation']->getRoute()->getRequirement('_method')
);
} }
return $methodA > $methodB ? 1 : -1; return $methodA > $methodB ? 1 : -1;
} }
return strcmp($a['route']->getPattern(), $b['route']->getPattern()); return strcmp(
$a['annotation']->getRoute()->getPattern(),
$b['annotation']->getRoute()->getPattern()
);
} }
return strcmp($a['resource'], $b['resource']); return strcmp($a['resource'], $b['resource']);
}); });
return $array; return $array;
} }
/** /**
* Returns the ReflectionMethod for the given controller string * Returns the ReflectionMethod for the given controller string.
* *
* @param string $controller * @param string $controller
* @return \ReflectionMethod|null * @return \ReflectionMethod|null
@ -130,7 +143,7 @@ class ApiDocExtractor
$method = $matches[2]; $method = $matches[2];
if ($this->container->has($controller)) { if ($this->container->has($controller)) {
$this->container->enterScope('request'); $this->container->enterScope('request');
$this->container->set('request', new Request); $this->container->set('request', new Request());
$class = get_class($this->container->get($controller)); $class = get_class($this->container->get($controller));
$this->container->leaveScope('request'); $this->container->leaveScope('request');
} }
@ -147,20 +160,18 @@ class ApiDocExtractor
} }
/** /**
* Returns an array containing two values with the following keys: * Returns an ApiDoc annotation.
* - annotation
* - route
* *
* @param string $controller * @param string $controller
* @param Route $route * @param Route $route
* @return array|null * @return ApiDoc|null
*/ */
public function get($controller, $route) public function get($controller, $route)
{ {
if ($method = $this->getReflectionMethod($controller)) { if ($method = $this->getReflectionMethod($controller)) {
if ($annot = $this->reader->getMethodAnnotation($method, self::ANNOTATION_CLASS)) { if ($annotation = $this->reader->getMethodAnnotation($method, self::ANNOTATION_CLASS)) {
if ($route = $this->router->getRouteCollection()->get($route)) { if ($route = $this->router->getRouteCollection()->get($route)) {
return $this->parseAnnotations($annot, $method, $route); return $this->extractData($annotation, $route, $method);
} }
} }
} }
@ -168,45 +179,26 @@ class ApiDocExtractor
return null; return null;
} }
protected function parseAnnotations($annotation, $method, $route)
{
$data = $this->getData($annotation, $route, $method);
foreach ($this->reader->getMethodAnnotations($method) as $annot) {
if (is_subclass_of($annot, self::FOS_REST_PARAM_CLASS)) {
if ($annot->strict) {
$data['requirements'][$annot->name] = array(
'requirement' => $annot->requirements,
'type' => '',
'description' => $annot->description,
);
} else {
$data['annotation']->addFilter($annot->name, array(
'requirement' => $annot->requirements,
'description' => $annot->description,
));
}
}
}
return $data;
}
/** /**
* Allows to add more data to the ApiDoc object, and * Returns a new ApiDoc instance with more data.
* returns an array containing the following keys:
* - annotation
* - route
* *
* @param ApiDoc $annotation * @param ApiDoc $annotation
* @param Route $route * @param Route $route
* @param \ReflectionMethod $method * @param \ReflectionMethod $method
* @return array * @return ApiDoc
*/ */
protected function getData(ApiDoc $annotation, Route $route, \ReflectionMethod $method) protected function extractData(ApiDoc $annotation, Route $route, \ReflectionMethod $method)
{ {
$docblock = $this->getDocComment($method); // create a new annotation
$annotation = clone $annotation;
// parse annotations
$this->parseAnnotations($annotation, $route, $method);
// route
$annotation->setRoute($route);
// description
if (null === $annotation->getDescription()) { if (null === $annotation->getDescription()) {
$comments = explode("\n", $this->getDocCommentText($method)); $comments = explode("\n", $this->getDocCommentText($method));
// just set the first line // just set the first line
@ -220,20 +212,77 @@ class ApiDocExtractor
} }
} }
// doc
$annotation->setDocumentation($this->getDocCommentText($method)); $annotation->setDocumentation($this->getDocCommentText($method));
// formType
if (null !== $formType = $annotation->getFormType()) {
$parameters = $this->parser->parse($formType);
if ('PUT' === $method) {
// All parameters are optional with PUT (update)
array_walk($parameters, function($val, $key) use (&$data) {
$parameters[$key]['required'] = false;
});
}
$annotation->setParameters($parameters);
}
// requirements
$requirements = array();
foreach ($route->compile()->getRequirements() as $name => $value) {
if ('_method' !== $name) {
$requirements[$name] = array(
'requirement' => $value,
'type' => '',
'description' => '',
);
}
}
$paramDocs = array(); $paramDocs = array();
foreach (explode("\n", $docblock) as $line) { foreach (explode("\n", $this->getDocComment($method)) as $line) {
if (preg_match('{^@param (.+)}', trim($line), $matches)) { if (preg_match('{^@param (.+)}', trim($line), $matches)) {
$paramDocs[] = $matches[1]; $paramDocs[] = $matches[1];
} }
} }
$route->setOptions(array_merge($route->getOptions(), array('_paramDocs' => $paramDocs))); $regexp = '{(\w*) *\$%s *(.*)}i';
foreach ($route->compile()->getVariables() as $var) {
$found = false;
foreach ($paramDocs as $paramDoc) {
if (preg_match(sprintf($regexp, preg_quote($var)), $paramDoc, $matches)) {
$requirements[$var]['type'] = isset($matches[1]) ? $matches[1] : '';
$requirements[$var]['description'] = $matches[2];
return array('annotation' => $annotation, 'route' => $route, 'requirements' => array()); if (!isset($requirements[$var]['requirement'])) {
$requirements[$var]['requirement'] = '';
} }
$found = true;
break;
}
}
if (!isset($requirements[$var]) && false === $found) {
$requirements[$var] = array('requirement' => '', 'type' => '', 'description' => '');
}
}
$annotation->setRequirements($requirements);
// method/uri
$annotation->setMethod($route->getRequirement('_method') ?: 'ANY');
$annotation->setUri($route->compile()->getPattern());
return $annotation;
}
/**
* @param Reflector $reflected
* @return string
*/
protected function getDocComment(\Reflector $reflected) protected function getDocComment(\Reflector $reflected)
{ {
$comment = $reflected->getDocComment(); $comment = $reflected->getDocComment();
@ -250,6 +299,10 @@ class ApiDocExtractor
return $comment; return $comment;
} }
/**
* @param Reflector $reflected
* @return string
*/
protected function getDocCommentText(\Reflector $reflected) protected function getDocCommentText(\Reflector $reflected)
{ {
$comment = $reflected->getDocComment(); $comment = $reflected->getDocComment();
@ -264,4 +317,32 @@ class ApiDocExtractor
return trim($comment); return trim($comment);
} }
/**
* Parses annotations for a given method, and adds new information to the given ApiDoc
* annotation. Useful to extract information from the FOSRestBundle annotations.
*
* @param ApiDoc $annotation
* @param Route $route
* @param ReflectionMethod $method
*/
protected function parseAnnotations(ApiDoc $annotation, Route $route, \ReflectionMethod $method)
{
foreach ($this->reader->getMethodAnnotations($method) as $annot) {
if (is_subclass_of($annot, self::FOS_REST_PARAM_CLASS)) {
if ($annot->strict) {
$annotation->addRequirement($annot->name, array(
'requirement' => $annot->requirements,
'type' => '',
'description' => $annot->description,
));
} else {
$annotation->addFilter($annot->name, array(
'requirement' => $annot->requirements,
'description' => $annot->description,
));
}
}
}
}
} }

View File

@ -12,27 +12,15 @@
namespace Nelmio\ApiDocBundle\Formatter; namespace Nelmio\ApiDocBundle\Formatter;
use Nelmio\ApiDocBundle\Annotation\ApiDoc; use Nelmio\ApiDocBundle\Annotation\ApiDoc;
use Nelmio\ApiDocBundle\Parser\FormTypeParser;
use Symfony\Component\Routing\Route;
abstract class AbstractFormatter implements FormatterInterface abstract class AbstractFormatter implements FormatterInterface
{ {
/**
* @var \Nelmio\ApiDocBundle\Parser\FormTypeParser
*/
protected $parser;
public function __construct(FormTypeParser $parser)
{
$this->parser = $parser;
}
/** /**
* {@inheritdoc} * {@inheritdoc}
*/ */
public function formatOne(ApiDoc $apiDoc, Route $route) public function formatOne(ApiDoc $annotation)
{ {
return $this->renderOne($this->getData($apiDoc, $route)); return $this->renderOne($annotation->toArray());
} }
/** /**
@ -42,12 +30,7 @@ abstract class AbstractFormatter implements FormatterInterface
{ {
$array = array(); $array = array();
foreach ($collection as $coll) { foreach ($collection as $coll) {
$resource = $coll['resource']; $array[$coll['resource']][] = $coll['annotation']->toArray();
if (!isset($array[$resource])) {
$array[$resource] = array();
}
$array[$resource][] = $this->getData($coll['annotation'], $coll['route'], $coll['requirements']);
} }
return $this->render($array); return $this->render($array);
@ -68,80 +51,4 @@ abstract class AbstractFormatter implements FormatterInterface
* @return string|array * @return string|array
*/ */
abstract protected function render(array $collection); abstract protected function render(array $collection);
/**
* @param ApiDoc $apiDoc
* @param Route $route
* @param array $requirements
* @return array
*/
protected function getData(ApiDoc $apiDoc, Route $route, array $requirements = array())
{
$method = $route->getRequirement('_method');
$data = array(
'method' => $method ?: 'ANY',
'uri' => $route->compile()->getPattern(),
);
foreach ($route->compile()->getRequirements() as $name => $value) {
if ('_method' !== $name) {
$requirements[$name] = array(
'requirement' => $value,
'type' => '',
'description' => '',
);
}
}
if (null !== $paramDocs = $route->getOption('_paramDocs')) {
$regexp = '{(\w*) *\$%s *(.*)}i';
foreach ($route->compile()->getVariables() as $var) {
$found = false;
foreach ($paramDocs as $paramDoc) {
if (preg_match(sprintf($regexp, preg_quote($var)), $paramDoc, $matches)) {
$requirements[$var]['type'] = isset($matches[1]) ? $matches[1] : '';
$requirements[$var]['description'] = $matches[2];
if (!isset($requirements[$var]['requirement'])) {
$requirements[$var]['requirement'] = '';
}
$found = true;
break;
}
}
if (!isset($requirements[$var]) && false === $found) {
$requirements[$var] = array('requirement' => '', 'type' => '', 'description' => '');
}
}
}
$data['requirements'] = $requirements;
if (null !== $formType = $apiDoc->getFormType()) {
$data['parameters'] = $this->parser->parse($formType);
if ('PUT' === $method) {
// All parameters are optional with PUT (update)
array_walk($data['parameters'], function($val, $key) use (&$data) {
$data['parameters'][$key]['required'] = false;
});
}
}
if ($filters = $apiDoc->getFilters()) {
$data['filters'] = $filters;
}
if ($description = $apiDoc->getDescription()) {
$data['description'] = $description;
}
if ($documentation = $apiDoc->getDocumentation()) {
$data['documentation'] = $documentation;
}
return $data;
}
} }

View File

@ -12,7 +12,6 @@
namespace Nelmio\ApiDocBundle\Formatter; namespace Nelmio\ApiDocBundle\Formatter;
use Nelmio\ApiDocBundle\Annotation\ApiDoc; use Nelmio\ApiDocBundle\Annotation\ApiDoc;
use Symfony\Component\Routing\Route;
interface FormatterInterface interface FormatterInterface
{ {
@ -27,9 +26,8 @@ interface FormatterInterface
/** /**
* Format documentation data for one route. * Format documentation data for one route.
* *
* @param ApiDoc $apiDoc * @param ApiDoc $annotation
* @param Route $route
* return string|array * return string|array
*/ */
public function formatOne(ApiDoc $apiDoc, Route $route); public function formatOne(ApiDoc $annotation);
} }

View File

@ -1,4 +1,4 @@
Copyright (c) 2011 Nelmio Copyright (c) 2012 Nelmio
Permission is hereby granted, free of charge, to any person obtaining a copy Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal of this software and associated documentation files (the "Software"), to deal

View File

@ -15,9 +15,7 @@
<service id="nelmio_api_doc.parser.form_type_parser" class="%nelmio_api_doc.parser.form_type_parser.class%"> <service id="nelmio_api_doc.parser.form_type_parser" class="%nelmio_api_doc.parser.form_type_parser.class%">
<argument type="service" id="form.factory" /> <argument type="service" id="form.factory" />
</service> </service>
<service id="nelmio_api_doc.formatter.abstract_formatter" class="%nelmio_api_doc.formatter.abstract_formatter.class%"> <service id="nelmio_api_doc.formatter.abstract_formatter" class="%nelmio_api_doc.formatter.abstract_formatter.class%" />
<argument type="service" id="nelmio_api_doc.parser.form_type_parser" />
</service>
<service id="nelmio_api_doc.formatter.markdown_formatter" class="%nelmio_api_doc.formatter.markdown_formatter.class%" <service id="nelmio_api_doc.formatter.markdown_formatter" class="%nelmio_api_doc.formatter.markdown_formatter.class%"
parent="nelmio_api_doc.formatter.abstract_formatter" /> parent="nelmio_api_doc.formatter.abstract_formatter" />
<service id="nelmio_api_doc.formatter.simple_formatter" class="%nelmio_api_doc.formatter.simple_formatter.class%" <service id="nelmio_api_doc.formatter.simple_formatter" class="%nelmio_api_doc.formatter.simple_formatter.class%"

View File

@ -13,6 +13,7 @@
<argument type="service" id="service_container"/> <argument type="service" id="service_container"/>
<argument type="service" id="router" /> <argument type="service" id="router" />
<argument type="service" id="annotation_reader" /> <argument type="service" id="annotation_reader" />
<argument type="service" id="nelmio_api_doc.parser.form_type_parser" />
</service> </service>
<service id="nelmio_api_doc.form.extension.description_form_type_extension" class="%nelmio_api_doc.form.extension.description_form_type_extension.class%"> <service id="nelmio_api_doc.form.extension.description_form_type_extension" class="%nelmio_api_doc.form.extension.description_form_type_extension.class%">
<tag name="form.type_extension" alias="form" /> <tag name="form.type_extension" alias="form" />

View File

@ -21,11 +21,12 @@ class ApiDocTest extends TestCase
$data = array(); $data = array();
$annot = new ApiDoc($data); $annot = new ApiDoc($data);
$array = $annot->toArray();
$this->assertTrue(is_array($annot->getFilters())); $this->assertTrue(is_array($array));
$this->assertCount(0, $annot->getFilters()); $this->assertFalse(isset($array['filters']));
$this->assertFalse($annot->isResource()); $this->assertFalse($annot->isResource());
$this->assertNull($annot->getDescription()); $this->assertFalse(isset($array['description']));
$this->assertNull($annot->getFormType()); $this->assertNull($annot->getFormType());
} }
@ -37,11 +38,12 @@ class ApiDocTest extends TestCase
); );
$annot = new ApiDoc($data); $annot = new ApiDoc($data);
$array = $annot->toArray();
$this->assertTrue(is_array($annot->getFilters())); $this->assertTrue(is_array($array));
$this->assertCount(0, $annot->getFilters()); $this->assertFalse(isset($array['filters']));
$this->assertFalse($annot->isResource()); $this->assertFalse($annot->isResource());
$this->assertNull($annot->getDescription()); $this->assertFalse(isset($array['description']));
$this->assertNull($annot->getFormType()); $this->assertNull($annot->getFormType());
} }
@ -52,11 +54,12 @@ class ApiDocTest extends TestCase
); );
$annot = new ApiDoc($data); $annot = new ApiDoc($data);
$array = $annot->toArray();
$this->assertTrue(is_array($annot->getFilters())); $this->assertTrue(is_array($array));
$this->assertCount(0, $annot->getFilters()); $this->assertFalse(isset($array['filters']));
$this->assertFalse($annot->isResource()); $this->assertFalse($annot->isResource());
$this->assertEquals($data['description'], $annot->getDescription()); $this->assertEquals($data['description'], $array['description']);
$this->assertNull($annot->getFormType()); $this->assertNull($annot->getFormType());
} }
@ -68,11 +71,12 @@ class ApiDocTest extends TestCase
); );
$annot = new ApiDoc($data); $annot = new ApiDoc($data);
$array = $annot->toArray();
$this->assertTrue(is_array($annot->getFilters())); $this->assertTrue(is_array($array));
$this->assertCount(0, $annot->getFilters()); $this->assertFalse(isset($array['filters']));
$this->assertFalse($annot->isResource()); $this->assertFalse($annot->isResource());
$this->assertEquals($data['description'], $annot->getDescription()); $this->assertEquals($data['description'], $array['description']);
$this->assertEquals($data['formType'], $annot->getFormType()); $this->assertEquals($data['formType'], $annot->getFormType());
} }
@ -85,11 +89,12 @@ class ApiDocTest extends TestCase
); );
$annot = new ApiDoc($data); $annot = new ApiDoc($data);
$array = $annot->toArray();
$this->assertTrue(is_array($annot->getFilters())); $this->assertTrue(is_array($array));
$this->assertCount(0, $annot->getFilters()); $this->assertFalse(isset($array['filters']));
$this->assertTrue($annot->isResource()); $this->assertTrue($annot->isResource());
$this->assertEquals($data['description'], $annot->getDescription()); $this->assertEquals($data['description'], $array['description']);
$this->assertEquals($data['formType'], $annot->getFormType()); $this->assertEquals($data['formType'], $annot->getFormType());
} }
@ -102,11 +107,12 @@ class ApiDocTest extends TestCase
); );
$annot = new ApiDoc($data); $annot = new ApiDoc($data);
$array = $annot->toArray();
$this->assertTrue(is_array($annot->getFilters())); $this->assertTrue(is_array($array));
$this->assertCount(0, $annot->getFilters()); $this->assertFalse(isset($array['filters']));
$this->assertFalse($annot->isResource()); $this->assertFalse($annot->isResource());
$this->assertEquals($data['description'], $annot->getDescription()); $this->assertEquals($data['description'], $array['description']);
$this->assertEquals($data['formType'], $annot->getFormType()); $this->assertEquals($data['formType'], $annot->getFormType());
} }
@ -121,12 +127,14 @@ class ApiDocTest extends TestCase
); );
$annot = new ApiDoc($data); $annot = new ApiDoc($data);
$array = $annot->toArray();
$this->assertTrue(is_array($annot->getFilters())); $this->assertTrue(is_array($array));
$this->assertCount(1, $annot->getFilters()); $this->assertTrue(is_array($array['filters']));
$this->assertEquals(array('a-filter' => array()), $annot->getFilters()); $this->assertCount(1, $array['filters']);
$this->assertEquals(array('a-filter' => array()), $array['filters']);
$this->assertTrue($annot->isResource()); $this->assertTrue($annot->isResource());
$this->assertEquals($data['description'], $annot->getDescription()); $this->assertEquals($data['description'], $array['description']);
$this->assertNull($annot->getFormType()); $this->assertNull($annot->getFormType());
} }
@ -157,12 +165,12 @@ class ApiDocTest extends TestCase
); );
$annot = new ApiDoc($data); $annot = new ApiDoc($data);
$array = $annot->toArray();
$this->assertTrue(is_array($annot->getFilters())); $this->assertTrue(is_array($array));
$this->assertCount(0, $annot->getFilters()); $this->assertFalse(isset($array['filters']));
$this->assertEquals(array(), $annot->getFilters());
$this->assertTrue($annot->isResource()); $this->assertTrue($annot->isResource());
$this->assertEquals($data['description'], $annot->getDescription()); $this->assertEquals($data['description'], $array['description']);
$this->assertEquals($data['formType'], $annot->getFormType()); $this->assertEquals($data['formType'], $annot->getFormType());
} }
} }

View File

@ -22,41 +22,44 @@ class ApiDocExtractorTest extends WebTestCase
$data = $extractor->all(); $data = $extractor->all();
$this->assertTrue(is_array($data)); $this->assertTrue(is_array($data));
$this->assertCount(9, $data); $this->assertCount(10, $data);
foreach ($data as $d) { foreach ($data as $d) {
$this->assertTrue(is_array($d)); $this->assertTrue(is_array($d));
$this->assertArrayHasKey('annotation', $d); $this->assertArrayHasKey('annotation', $d);
$this->assertArrayHasKey('route', $d);
$this->assertArrayHasKey('resource', $d); $this->assertArrayHasKey('resource', $d);
$this->assertInstanceOf('Nelmio\ApiDocBundle\Annotation\ApiDoc', $d['annotation']); $this->assertInstanceOf('Nelmio\ApiDocBundle\Annotation\ApiDoc', $d['annotation']);
$this->assertInstanceOf('Symfony\Component\Routing\Route', $d['route']); $this->assertInstanceOf('Symfony\Component\Routing\Route', $d['annotation']->getRoute());
$this->assertNotNull($d['resource']); $this->assertNotNull($d['resource']);
} }
$a1 = $data[0]['annotation']; $a1 = $data[0]['annotation'];
$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($a1->getFilters())); $this->assertTrue(is_array($array1['filters']));
$this->assertNull($a1->getFormType()); $this->assertNull($a1->getFormType());
$a1 = $data[1]['annotation']; $a1 = $data[1]['annotation'];
$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($a1->getFilters())); $this->assertTrue(is_array($array1['filters']));
$this->assertNull($a1->getFormType()); $this->assertNull($a1->getFormType());
$a2 = $data[2]['annotation']; $a2 = $data[2]['annotation'];
$array2 = $a2->toArray();
$this->assertFalse($a2->isResource()); $this->assertFalse($a2->isResource());
$this->assertEquals('create test', $a2->getDescription()); $this->assertEquals('create test', $a2->getDescription());
$this->assertTrue(is_array($a2->getFilters())); $this->assertFalse(isset($array2['filters']));
$this->assertEquals('Nelmio\ApiDocBundle\Tests\Fixtures\Form\TestType', $a2->getFormType()); $this->assertEquals('Nelmio\ApiDocBundle\Tests\Fixtures\Form\TestType', $a2->getFormType());
$a2 = $data[3]['annotation']; $a2 = $data[3]['annotation'];
$array2 = $a2->toArray();
$this->assertFalse($a2->isResource()); $this->assertFalse($a2->isResource());
$this->assertEquals('create test', $a2->getDescription()); $this->assertEquals('create test', $a2->getDescription());
$this->assertTrue(is_array($a2->getFilters())); $this->assertFalse(isset($array2['filters']));
$this->assertEquals('Nelmio\ApiDocBundle\Tests\Fixtures\Form\TestType', $a2->getFormType()); $this->assertEquals('Nelmio\ApiDocBundle\Tests\Fixtures\Form\TestType', $a2->getFormType());
} }
@ -64,20 +67,22 @@ class ApiDocExtractorTest extends WebTestCase
{ {
$container = $this->getContainer(); $container = $this->getContainer();
$extractor = $container->get('nelmio_api_doc.extractor.api_doc_extractor'); $extractor = $container->get('nelmio_api_doc.extractor.api_doc_extractor');
$data = $extractor->get('Nelmio\ApiDocBundle\Tests\Fixtures\Controller\TestController::indexAction', 'test_route_1'); $annotation = $extractor->get('Nelmio\ApiDocBundle\Tests\Fixtures\Controller\TestController::indexAction', 'test_route_1');
$this->assertTrue(isset($data['route'])); $this->assertInstanceOf('Nelmio\ApiDocBundle\Annotation\ApiDoc', $annotation);
$this->assertTrue(isset($data['annotation']));
$a = $data['annotation']; $this->assertTrue($annotation->isResource());
$this->assertTrue($a->isResource()); $this->assertEquals('index action', $annotation->getDescription());
$this->assertEquals('index action', $a->getDescription());
$this->assertTrue(is_array($a->getFilters()));
$this->assertNull($a->getFormType());
$data2 = $extractor->get('nemlio.test.controller:indexAction', 'test_service_route_1'); $array = $annotation->toArray();
$data2['route']->setDefault('_controller', $data['route']->getDefault('_controller')); $this->assertTrue(is_array($array['filters']));
$this->assertEquals($data, $data2); $this->assertNull($annotation->getFormType());
$annotation2 = $extractor->get('nemlio.test.controller:indexAction', 'test_service_route_1');
$annotation2->getRoute()
->setDefault('_controller', $annotation->getRoute()->getDefault('_controller'))
->compile(); // compile as we changed a default value
$this->assertEquals($annotation, $annotation2);
} }
public function testGetWithBadController() public function testGetWithBadController()
@ -136,12 +141,12 @@ class ApiDocExtractorTest extends WebTestCase
{ {
$container = $this->getContainer(); $container = $this->getContainer();
$extractor = $container->get('nelmio_api_doc.extractor.api_doc_extractor'); $extractor = $container->get('nelmio_api_doc.extractor.api_doc_extractor');
$data = $extractor->get('Nelmio\ApiDocBundle\Tests\Fixtures\Controller\TestController::myCommentedAction', 'test_route_5'); $annotation = $extractor->get('Nelmio\ApiDocBundle\Tests\Fixtures\Controller\TestController::myCommentedAction', 'test_route_5');
$this->assertNotNull($data); $this->assertNotNull($annotation);
$this->assertEquals( $this->assertEquals(
"This method is useful to test if the getDocComment works.", "This method is useful to test if the getDocComment works.",
$data['annotation']->getDescription() $annotation->getDescription()
); );
} }
} }

View File

@ -11,6 +11,7 @@
namespace Nelmio\ApiDocBundle\Tests\Fixtures\Controller; namespace Nelmio\ApiDocBundle\Tests\Fixtures\Controller;
use FOS\RestBundle\Controller\Annotations\QueryParam;
use Nelmio\ApiDocBundle\Annotation\ApiDoc; use Nelmio\ApiDocBundle\Annotation\ApiDoc;
use Symfony\Component\HttpFoundation\Response; use Symfony\Component\HttpFoundation\Response;
@ -81,4 +82,12 @@ class TestController
public function anotherPostAction() public function anotherPostAction()
{ {
} }
/**
* @ApiDoc()
* @QueryParam(name="page", requirements="\d+", default="1", description="Page of the overview.")
*/
public function zActionWithQueryParamAction()
{
}
} }

View File

@ -34,6 +34,12 @@ test_route_7:
requirements: requirements:
_method: POST _method: POST
test_route_8:
pattern: /z-action-with-query-param
defaults: { _controller: NelmioApiDocTestBundle:Test:zActionWithQueryParam }
requirements:
_method: GET
test_service_route_1: test_service_route_1:
pattern: /tests pattern: /tests
defaults: { _controller: nemlio.test.controller:indexAction, _format: json } defaults: { _controller: nemlio.test.controller:indexAction, _format: json }

View File

@ -158,6 +158,17 @@ _This method is useful to test if the getDocComment works._
**id** **id**
- Requirement: \d+ - Requirement: \d+
### `GET` /z-action-with-query-param ###
#### Filters ####
page:
* requirement: \d+
* description: Page of the overview.
MARKDOWN; MARKDOWN;
$this->assertEquals($expected, $result); $this->assertEquals($expected, $result);
@ -168,8 +179,8 @@ MARKDOWN;
$container = $this->getContainer(); $container = $this->getContainer();
$extractor = $container->get('nelmio_api_doc.extractor.api_doc_extractor'); $extractor = $container->get('nelmio_api_doc.extractor.api_doc_extractor');
$data = $extractor->get('Nelmio\ApiDocBundle\Tests\Fixtures\Controller\TestController::indexAction', 'test_route_1'); $annotation = $extractor->get('Nelmio\ApiDocBundle\Tests\Fixtures\Controller\TestController::indexAction', 'test_route_1');
$result = $container->get('nelmio_api_doc.formatter.markdown_formatter')->formatOne($data['annotation'], $data['route']); $result = $container->get('nelmio_api_doc.formatter.markdown_formatter')->formatOne($annotation);
$expected = <<<MARKDOWN $expected = <<<MARKDOWN
### `GET` /tests ### ### `GET` /tests ###

View File

@ -30,9 +30,6 @@ class SimpleFormatterTest extends WebTestCase
array( array(
'method' => 'GET', 'method' => 'GET',
'uri' => '/tests', 'uri' => '/tests',
'requirements' =>
array(
),
'filters' => 'filters' =>
array( array(
'a' => 'a' =>
@ -55,9 +52,6 @@ class SimpleFormatterTest extends WebTestCase
array( array(
'method' => 'GET', 'method' => 'GET',
'uri' => '/tests', 'uri' => '/tests',
'requirements' =>
array(
),
'filters' => 'filters' =>
array( array(
'a' => 'a' =>
@ -80,9 +74,6 @@ class SimpleFormatterTest extends WebTestCase
array( array(
'method' => 'POST', 'method' => 'POST',
'uri' => '/tests', 'uri' => '/tests',
'requirements' =>
array(
),
'parameters' => 'parameters' =>
array( array(
'a' => 'a' =>
@ -110,9 +101,6 @@ class SimpleFormatterTest extends WebTestCase
array( array(
'method' => 'POST', 'method' => 'POST',
'uri' => '/tests', 'uri' => '/tests',
'requirements' =>
array(
),
'parameters' => 'parameters' =>
array( array(
'a' => 'a' =>
@ -143,9 +131,6 @@ class SimpleFormatterTest extends WebTestCase
array( array(
'method' => 'POST', 'method' => 'POST',
'uri' => '/another-post', 'uri' => '/another-post',
'requirements' =>
array(
),
'parameters' => 'parameters' =>
array( array(
'a' => 'a' =>
@ -161,9 +146,6 @@ class SimpleFormatterTest extends WebTestCase
array( array(
'method' => 'ANY', 'method' => 'ANY',
'uri' => '/any', 'uri' => '/any',
'requirements' =>
array(
),
'description' => 'Action without HTTP verb', 'description' => 'Action without HTTP verb',
), ),
2 => 2 =>
@ -197,6 +179,15 @@ class SimpleFormatterTest extends WebTestCase
'id' => array('type' => '', 'description' => '', 'requirement' => '\d+') 'id' => array('type' => '', 'description' => '', 'requirement' => '\d+')
), ),
), ),
5 =>
array(
'method' => 'GET',
'uri' => '/z-action-with-query-param',
'filters' =>
array(
'page' => array('description' => 'Page of the overview.', 'requirement' => '\d+')
),
),
), ),
); );
@ -208,13 +199,12 @@ class SimpleFormatterTest extends WebTestCase
$container = $this->getContainer(); $container = $this->getContainer();
$extractor = $container->get('nelmio_api_doc.extractor.api_doc_extractor'); $extractor = $container->get('nelmio_api_doc.extractor.api_doc_extractor');
$data = $extractor->get('Nelmio\ApiDocBundle\Tests\Fixtures\Controller\TestController::indexAction', 'test_route_1'); $annotation = $extractor->get('Nelmio\ApiDocBundle\Tests\Fixtures\Controller\TestController::indexAction', 'test_route_1');
$result = $container->get('nelmio_api_doc.formatter.simple_formatter')->formatOne($data['annotation'], $data['route']); $result = $container->get('nelmio_api_doc.formatter.simple_formatter')->formatOne($annotation);
$expected = array( $expected = array(
'method' => 'GET', 'method' => 'GET',
'uri' => '/tests', 'uri' => '/tests',
'requirements' => array(),
'filters' => array( 'filters' => array(
'a' => array( 'a' => array(
'dataType' => 'integer', 'dataType' => 'integer',