Merge pull request #49 from willdurand/refactoring

Refactoring
This commit is contained in:
Jordi Boggiano 2012-07-20 01:19:52 -07:00
commit b49ea21c36
17 changed files with 383 additions and 269 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

@ -46,6 +46,7 @@ class MarkdownFormatter extends AbstractFormatter
if (!empty($infos['type'])) { if (!empty($infos['type'])) {
$markdown .= sprintf(" - Type: %s\n", $infos['type']); $markdown .= sprintf(" - Type: %s\n", $infos['type']);
} }
if (!empty($infos['description'])) { if (!empty($infos['description'])) {
$markdown .= sprintf(" - Description: %s\n", $infos['description']); $markdown .= sprintf(" - Description: %s\n", $infos['description']);
} }
@ -61,7 +62,7 @@ class MarkdownFormatter extends AbstractFormatter
$markdown .= sprintf("%s:\n\n", $name); $markdown .= sprintf("%s:\n\n", $name);
foreach ($filter as $key => $value) { foreach ($filter as $key => $value) {
$markdown .= sprintf(" * %s: %s\n", $key, trim(json_encode($value), '"')); $markdown .= sprintf(" * %s: %s\n", ucwords($key), trim(str_replace('\\\\', '\\', json_encode($value)), '"'));
} }
$markdown .= "\n"; $markdown .= "\n";

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

@ -262,7 +262,6 @@ li.operation div.heading h3 span.path {
li.operation div.content { li.operation div.content {
border: 1px solid black; border: 1px solid black;
border-top: none;
padding: 10px; padding: 10px;
-moz-border-radius-bottomleft: 6px; -moz-border-radius-bottomleft: 6px;
-webkit-border-bottom-left-radius: 6px; -webkit-border-bottom-left-radius: 6px;
@ -445,7 +444,6 @@ li.operation.patch div.content h4 {
.panes { .panes {
clear: both; clear: both;
border-top: 1px solid #C3D9EC;
} }
.pane.sandbox { .pane.sandbox {

View File

@ -72,7 +72,7 @@
{% for key, value in infos %} {% for key, value in infos %}
<tr> <tr>
<td>{{ key|title }}</td> <td>{{ key|title }}</td>
<td>{{ value|json_encode|trim('"') }}</td> <td>{{ value|json_encode|replace({'\\\\': '\\'})|trim('"') }}</td>
</tr> </tr>
{% endfor %} {% endfor %}
</table> </table>

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

@ -34,12 +34,12 @@ _index action_
a: a:
* dataType: integer * DataType: integer
b: b:
* dataType: string * DataType: string
* arbitrary: ["arg1","arg2"] * Arbitrary: ["arg1","arg2"]
### `GET` /tests ### ### `GET` /tests ###
@ -50,12 +50,12 @@ _index action_
a: a:
* dataType: integer * DataType: integer
b: b:
* dataType: string * DataType: string
* arbitrary: ["arg1","arg2"] * Arbitrary: ["arg1","arg2"]
### `POST` /tests ### ### `POST` /tests ###
@ -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 ###
@ -180,12 +191,12 @@ _index action_
a: a:
* dataType: integer * DataType: integer
b: b:
* dataType: string * DataType: string
* arbitrary: ["arg1","arg2"] * Arbitrary: ["arg1","arg2"]
MARKDOWN; MARKDOWN;

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',