mirror of
https://github.com/retailcrm/NelmioApiDocBundle.git
synced 2025-02-02 15:51:48 +03:00
commit
b49ea21c36
@ -11,6 +11,8 @@
|
||||
|
||||
namespace Nelmio\ApiDocBundle\Annotation;
|
||||
|
||||
use Symfony\Component\Routing\Route;
|
||||
|
||||
/**
|
||||
* @Annotation
|
||||
*/
|
||||
@ -41,6 +43,31 @@ class ApiDoc
|
||||
*/
|
||||
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)
|
||||
{
|
||||
if (isset($data['formType'])) {
|
||||
@ -65,14 +92,6 @@ class ApiDoc
|
||||
$this->isResource = isset($data['resource']) && $data['resource'];
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
public function getFilters()
|
||||
{
|
||||
return $this->filters;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $name
|
||||
* @param array $filter
|
||||
@ -82,6 +101,23 @@ class ApiDoc
|
||||
$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
|
||||
*/
|
||||
@ -91,7 +127,7 @@ class ApiDoc
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string|null
|
||||
* @return string
|
||||
*/
|
||||
public function getDescription()
|
||||
{
|
||||
@ -106,14 +142,6 @@ class ApiDoc
|
||||
$this->description = $description;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string|null
|
||||
*/
|
||||
public function getDocumentation()
|
||||
{
|
||||
return $this->documentation;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $documentation
|
||||
*/
|
||||
@ -129,4 +157,77 @@ class ApiDoc
|
||||
{
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
@ -54,8 +54,8 @@ class RequestListener
|
||||
$controller = $request->attributes->get('_controller');
|
||||
$route = $request->attributes->get('_route');
|
||||
|
||||
if (null !== $array = $this->extractor->get($controller, $route)) {
|
||||
$result = $this->formatter->formatOne($array['annotation'], $array['route']);
|
||||
if (null !== $annotation = $this->extractor->get($controller, $route)) {
|
||||
$result = $this->formatter->formatOne($annotation);
|
||||
|
||||
$event->setResponse(new Response($result, 200, array(
|
||||
'Content-Type' => 'text/html'
|
||||
|
@ -13,6 +13,7 @@ namespace Nelmio\ApiDocBundle\Extractor;
|
||||
|
||||
use Doctrine\Common\Annotations\Reader;
|
||||
use Nelmio\ApiDocBundle\Annotation\ApiDoc;
|
||||
use Nelmio\ApiDocBundle\Parser\FormTypeParser;
|
||||
use Symfony\Component\Routing\Route;
|
||||
use Symfony\Component\Routing\RouterInterface;
|
||||
use Symfony\Component\DependencyInjection\ContainerInterface;
|
||||
@ -39,17 +40,22 @@ class ApiDocExtractor
|
||||
*/
|
||||
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->router = $router;
|
||||
$this->reader = $reader;
|
||||
$this->router = $router;
|
||||
$this->reader = $reader;
|
||||
$this->parser = $parser;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an array of data where each data is an array with the following keys:
|
||||
* - annotation
|
||||
* - route
|
||||
* - resource
|
||||
*
|
||||
* @return array
|
||||
@ -61,12 +67,12 @@ class ApiDocExtractor
|
||||
|
||||
foreach ($this->router->getRouteCollection()->all() as $route) {
|
||||
if ($method = $this->getReflectionMethod($route->getDefault('_controller'))) {
|
||||
if ($annot = $this->reader->getMethodAnnotation($method, self::ANNOTATION_CLASS)) {
|
||||
if ($annot->isResource()) {
|
||||
if ($annotation = $this->reader->getMethodAnnotation($method, self::ANNOTATION_CLASS)) {
|
||||
if ($annotation->isResource()) {
|
||||
$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);
|
||||
foreach ($array as $index => $element) {
|
||||
$hasResource = false;
|
||||
$pattern = $element['route']->getPattern();
|
||||
$pattern = $element['annotation']->getRoute()->getPattern();
|
||||
|
||||
foreach ($resources as $resource) {
|
||||
if (0 === strpos($pattern, $resource)) {
|
||||
@ -91,31 +97,38 @@ class ApiDocExtractor
|
||||
}
|
||||
|
||||
$methodOrder = array('GET', 'POST', 'PUT', 'DELETE');
|
||||
|
||||
usort($array, function($a, $b) use ($methodOrder) {
|
||||
if ($a['resource'] === $b['resource']) {
|
||||
if ($a['route']->getPattern() === $b['route']->getPattern()) {
|
||||
$methodA = array_search($a['route']->getRequirement('_method'), $methodOrder);
|
||||
$methodB = array_search($b['route']->getRequirement('_method'), $methodOrder);
|
||||
if ($a['annotation']->getRoute()->getPattern() === $b['annotation']->getRoute()->getPattern()) {
|
||||
$methodA = array_search($a['annotation']->getRoute()->getRequirement('_method'), $methodOrder);
|
||||
$methodB = array_search($b['annotation']->getRoute()->getRequirement('_method'), $methodOrder);
|
||||
|
||||
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 strcmp($a['route']->getPattern(), $b['route']->getPattern());
|
||||
return strcmp(
|
||||
$a['annotation']->getRoute()->getPattern(),
|
||||
$b['annotation']->getRoute()->getPattern()
|
||||
);
|
||||
}
|
||||
|
||||
return strcmp($a['resource'], $b['resource']);
|
||||
});
|
||||
|
||||
|
||||
|
||||
return $array;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the ReflectionMethod for the given controller string
|
||||
* Returns the ReflectionMethod for the given controller string.
|
||||
*
|
||||
* @param string $controller
|
||||
* @return \ReflectionMethod|null
|
||||
@ -130,7 +143,7 @@ class ApiDocExtractor
|
||||
$method = $matches[2];
|
||||
if ($this->container->has($controller)) {
|
||||
$this->container->enterScope('request');
|
||||
$this->container->set('request', new Request);
|
||||
$this->container->set('request', new Request());
|
||||
$class = get_class($this->container->get($controller));
|
||||
$this->container->leaveScope('request');
|
||||
}
|
||||
@ -147,20 +160,18 @@ class ApiDocExtractor
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an array containing two values with the following keys:
|
||||
* - annotation
|
||||
* - route
|
||||
* Returns an ApiDoc annotation.
|
||||
*
|
||||
* @param string $controller
|
||||
* @param Route $route
|
||||
* @return array|null
|
||||
* @return ApiDoc|null
|
||||
*/
|
||||
public function get($controller, $route)
|
||||
{
|
||||
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)) {
|
||||
return $this->parseAnnotations($annot, $method, $route);
|
||||
return $this->extractData($annotation, $route, $method);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -168,45 +179,26 @@ class ApiDocExtractor
|
||||
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 an array containing the following keys:
|
||||
* - annotation
|
||||
* - route
|
||||
* Returns a new ApiDoc instance with more data.
|
||||
*
|
||||
* @param ApiDoc $annotation
|
||||
* @param Route $route
|
||||
* @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()) {
|
||||
$comments = explode("\n", $this->getDocCommentText($method));
|
||||
// just set the first line
|
||||
@ -220,20 +212,77 @@ class ApiDocExtractor
|
||||
}
|
||||
}
|
||||
|
||||
// doc
|
||||
$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();
|
||||
foreach (explode("\n", $docblock) as $line) {
|
||||
foreach (explode("\n", $this->getDocComment($method)) as $line) {
|
||||
if (preg_match('{^@param (.+)}', trim($line), $matches)) {
|
||||
$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)
|
||||
{
|
||||
$comment = $reflected->getDocComment();
|
||||
@ -250,6 +299,10 @@ class ApiDocExtractor
|
||||
return $comment;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Reflector $reflected
|
||||
* @return string
|
||||
*/
|
||||
protected function getDocCommentText(\Reflector $reflected)
|
||||
{
|
||||
$comment = $reflected->getDocComment();
|
||||
@ -264,4 +317,32 @@ class ApiDocExtractor
|
||||
|
||||
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,
|
||||
));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -12,27 +12,15 @@
|
||||
namespace Nelmio\ApiDocBundle\Formatter;
|
||||
|
||||
use Nelmio\ApiDocBundle\Annotation\ApiDoc;
|
||||
use Nelmio\ApiDocBundle\Parser\FormTypeParser;
|
||||
use Symfony\Component\Routing\Route;
|
||||
|
||||
abstract class AbstractFormatter implements FormatterInterface
|
||||
{
|
||||
/**
|
||||
* @var \Nelmio\ApiDocBundle\Parser\FormTypeParser
|
||||
*/
|
||||
protected $parser;
|
||||
|
||||
public function __construct(FormTypeParser $parser)
|
||||
{
|
||||
$this->parser = $parser;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@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();
|
||||
foreach ($collection as $coll) {
|
||||
$resource = $coll['resource'];
|
||||
if (!isset($array[$resource])) {
|
||||
$array[$resource] = array();
|
||||
}
|
||||
|
||||
$array[$resource][] = $this->getData($coll['annotation'], $coll['route'], $coll['requirements']);
|
||||
$array[$coll['resource']][] = $coll['annotation']->toArray();
|
||||
}
|
||||
|
||||
return $this->render($array);
|
||||
@ -68,80 +51,4 @@ abstract class AbstractFormatter implements FormatterInterface
|
||||
* @return string|array
|
||||
*/
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
@ -12,7 +12,6 @@
|
||||
namespace Nelmio\ApiDocBundle\Formatter;
|
||||
|
||||
use Nelmio\ApiDocBundle\Annotation\ApiDoc;
|
||||
use Symfony\Component\Routing\Route;
|
||||
|
||||
interface FormatterInterface
|
||||
{
|
||||
@ -27,9 +26,8 @@ interface FormatterInterface
|
||||
/**
|
||||
* Format documentation data for one route.
|
||||
*
|
||||
* @param ApiDoc $apiDoc
|
||||
* @param Route $route
|
||||
* @param ApiDoc $annotation
|
||||
* return string|array
|
||||
*/
|
||||
public function formatOne(ApiDoc $apiDoc, Route $route);
|
||||
public function formatOne(ApiDoc $annotation);
|
||||
}
|
||||
|
@ -46,6 +46,7 @@ class MarkdownFormatter extends AbstractFormatter
|
||||
if (!empty($infos['type'])) {
|
||||
$markdown .= sprintf(" - Type: %s\n", $infos['type']);
|
||||
}
|
||||
|
||||
if (!empty($infos['description'])) {
|
||||
$markdown .= sprintf(" - Description: %s\n", $infos['description']);
|
||||
}
|
||||
@ -61,7 +62,7 @@ class MarkdownFormatter extends AbstractFormatter
|
||||
$markdown .= sprintf("%s:\n\n", $name);
|
||||
|
||||
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";
|
||||
|
2
LICENSE
2
LICENSE
@ -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
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
|
@ -15,9 +15,7 @@
|
||||
<service id="nelmio_api_doc.parser.form_type_parser" class="%nelmio_api_doc.parser.form_type_parser.class%">
|
||||
<argument type="service" id="form.factory" />
|
||||
</service>
|
||||
<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.abstract_formatter" class="%nelmio_api_doc.formatter.abstract_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" />
|
||||
<service id="nelmio_api_doc.formatter.simple_formatter" class="%nelmio_api_doc.formatter.simple_formatter.class%"
|
||||
|
@ -13,6 +13,7 @@
|
||||
<argument type="service" id="service_container"/>
|
||||
<argument type="service" id="router" />
|
||||
<argument type="service" id="annotation_reader" />
|
||||
<argument type="service" id="nelmio_api_doc.parser.form_type_parser" />
|
||||
</service>
|
||||
<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" />
|
||||
|
@ -262,7 +262,6 @@ li.operation div.heading h3 span.path {
|
||||
|
||||
li.operation div.content {
|
||||
border: 1px solid black;
|
||||
border-top: none;
|
||||
padding: 10px;
|
||||
-moz-border-radius-bottomleft: 6px;
|
||||
-webkit-border-bottom-left-radius: 6px;
|
||||
@ -445,7 +444,6 @@ li.operation.patch div.content h4 {
|
||||
|
||||
.panes {
|
||||
clear: both;
|
||||
border-top: 1px solid #C3D9EC;
|
||||
}
|
||||
|
||||
.pane.sandbox {
|
||||
|
@ -72,7 +72,7 @@
|
||||
{% for key, value in infos %}
|
||||
<tr>
|
||||
<td>{{ key|title }}</td>
|
||||
<td>{{ value|json_encode|trim('"') }}</td>
|
||||
<td>{{ value|json_encode|replace({'\\\\': '\\'})|trim('"') }}</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</table>
|
||||
|
@ -21,11 +21,12 @@ class ApiDocTest extends TestCase
|
||||
$data = array();
|
||||
|
||||
$annot = new ApiDoc($data);
|
||||
$array = $annot->toArray();
|
||||
|
||||
$this->assertTrue(is_array($annot->getFilters()));
|
||||
$this->assertCount(0, $annot->getFilters());
|
||||
$this->assertTrue(is_array($array));
|
||||
$this->assertFalse(isset($array['filters']));
|
||||
$this->assertFalse($annot->isResource());
|
||||
$this->assertNull($annot->getDescription());
|
||||
$this->assertFalse(isset($array['description']));
|
||||
$this->assertNull($annot->getFormType());
|
||||
}
|
||||
|
||||
@ -37,11 +38,12 @@ class ApiDocTest extends TestCase
|
||||
);
|
||||
|
||||
$annot = new ApiDoc($data);
|
||||
$array = $annot->toArray();
|
||||
|
||||
$this->assertTrue(is_array($annot->getFilters()));
|
||||
$this->assertCount(0, $annot->getFilters());
|
||||
$this->assertTrue(is_array($array));
|
||||
$this->assertFalse(isset($array['filters']));
|
||||
$this->assertFalse($annot->isResource());
|
||||
$this->assertNull($annot->getDescription());
|
||||
$this->assertFalse(isset($array['description']));
|
||||
$this->assertNull($annot->getFormType());
|
||||
}
|
||||
|
||||
@ -52,11 +54,12 @@ class ApiDocTest extends TestCase
|
||||
);
|
||||
|
||||
$annot = new ApiDoc($data);
|
||||
$array = $annot->toArray();
|
||||
|
||||
$this->assertTrue(is_array($annot->getFilters()));
|
||||
$this->assertCount(0, $annot->getFilters());
|
||||
$this->assertTrue(is_array($array));
|
||||
$this->assertFalse(isset($array['filters']));
|
||||
$this->assertFalse($annot->isResource());
|
||||
$this->assertEquals($data['description'], $annot->getDescription());
|
||||
$this->assertEquals($data['description'], $array['description']);
|
||||
$this->assertNull($annot->getFormType());
|
||||
}
|
||||
|
||||
@ -68,11 +71,12 @@ class ApiDocTest extends TestCase
|
||||
);
|
||||
|
||||
$annot = new ApiDoc($data);
|
||||
$array = $annot->toArray();
|
||||
|
||||
$this->assertTrue(is_array($annot->getFilters()));
|
||||
$this->assertCount(0, $annot->getFilters());
|
||||
$this->assertTrue(is_array($array));
|
||||
$this->assertFalse(isset($array['filters']));
|
||||
$this->assertFalse($annot->isResource());
|
||||
$this->assertEquals($data['description'], $annot->getDescription());
|
||||
$this->assertEquals($data['description'], $array['description']);
|
||||
$this->assertEquals($data['formType'], $annot->getFormType());
|
||||
}
|
||||
|
||||
@ -85,11 +89,12 @@ class ApiDocTest extends TestCase
|
||||
);
|
||||
|
||||
$annot = new ApiDoc($data);
|
||||
$array = $annot->toArray();
|
||||
|
||||
$this->assertTrue(is_array($annot->getFilters()));
|
||||
$this->assertCount(0, $annot->getFilters());
|
||||
$this->assertTrue(is_array($array));
|
||||
$this->assertFalse(isset($array['filters']));
|
||||
$this->assertTrue($annot->isResource());
|
||||
$this->assertEquals($data['description'], $annot->getDescription());
|
||||
$this->assertEquals($data['description'], $array['description']);
|
||||
$this->assertEquals($data['formType'], $annot->getFormType());
|
||||
}
|
||||
|
||||
@ -102,11 +107,12 @@ class ApiDocTest extends TestCase
|
||||
);
|
||||
|
||||
$annot = new ApiDoc($data);
|
||||
$array = $annot->toArray();
|
||||
|
||||
$this->assertTrue(is_array($annot->getFilters()));
|
||||
$this->assertCount(0, $annot->getFilters());
|
||||
$this->assertTrue(is_array($array));
|
||||
$this->assertFalse(isset($array['filters']));
|
||||
$this->assertFalse($annot->isResource());
|
||||
$this->assertEquals($data['description'], $annot->getDescription());
|
||||
$this->assertEquals($data['description'], $array['description']);
|
||||
$this->assertEquals($data['formType'], $annot->getFormType());
|
||||
}
|
||||
|
||||
@ -121,12 +127,14 @@ class ApiDocTest extends TestCase
|
||||
);
|
||||
|
||||
$annot = new ApiDoc($data);
|
||||
$array = $annot->toArray();
|
||||
|
||||
$this->assertTrue(is_array($annot->getFilters()));
|
||||
$this->assertCount(1, $annot->getFilters());
|
||||
$this->assertEquals(array('a-filter' => array()), $annot->getFilters());
|
||||
$this->assertTrue(is_array($array));
|
||||
$this->assertTrue(is_array($array['filters']));
|
||||
$this->assertCount(1, $array['filters']);
|
||||
$this->assertEquals(array('a-filter' => array()), $array['filters']);
|
||||
$this->assertTrue($annot->isResource());
|
||||
$this->assertEquals($data['description'], $annot->getDescription());
|
||||
$this->assertEquals($data['description'], $array['description']);
|
||||
$this->assertNull($annot->getFormType());
|
||||
}
|
||||
|
||||
@ -157,12 +165,12 @@ class ApiDocTest extends TestCase
|
||||
);
|
||||
|
||||
$annot = new ApiDoc($data);
|
||||
$array = $annot->toArray();
|
||||
|
||||
$this->assertTrue(is_array($annot->getFilters()));
|
||||
$this->assertCount(0, $annot->getFilters());
|
||||
$this->assertEquals(array(), $annot->getFilters());
|
||||
$this->assertTrue(is_array($array));
|
||||
$this->assertFalse(isset($array['filters']));
|
||||
$this->assertTrue($annot->isResource());
|
||||
$this->assertEquals($data['description'], $annot->getDescription());
|
||||
$this->assertEquals($data['description'], $array['description']);
|
||||
$this->assertEquals($data['formType'], $annot->getFormType());
|
||||
}
|
||||
}
|
||||
|
@ -22,62 +22,67 @@ class ApiDocExtractorTest extends WebTestCase
|
||||
$data = $extractor->all();
|
||||
|
||||
$this->assertTrue(is_array($data));
|
||||
$this->assertCount(9, $data);
|
||||
$this->assertCount(10, $data);
|
||||
|
||||
foreach ($data as $d) {
|
||||
$this->assertTrue(is_array($d));
|
||||
$this->assertArrayHasKey('annotation', $d);
|
||||
$this->assertArrayHasKey('route', $d);
|
||||
$this->assertArrayHasKey('resource', $d);
|
||||
|
||||
$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']);
|
||||
}
|
||||
|
||||
$a1 = $data[0]['annotation'];
|
||||
$array1 = $a1->toArray();
|
||||
$this->assertTrue($a1->isResource());
|
||||
$this->assertEquals('index action', $a1->getDescription());
|
||||
$this->assertTrue(is_array($a1->getFilters()));
|
||||
$this->assertTrue(is_array($array1['filters']));
|
||||
$this->assertNull($a1->getFormType());
|
||||
|
||||
$a1 = $data[1]['annotation'];
|
||||
$array1 = $a1->toArray();
|
||||
$this->assertTrue($a1->isResource());
|
||||
$this->assertEquals('index action', $a1->getDescription());
|
||||
$this->assertTrue(is_array($a1->getFilters()));
|
||||
$this->assertTrue(is_array($array1['filters']));
|
||||
$this->assertNull($a1->getFormType());
|
||||
|
||||
$a2 = $data[2]['annotation'];
|
||||
$array2 = $a2->toArray();
|
||||
$this->assertFalse($a2->isResource());
|
||||
$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());
|
||||
|
||||
$a2 = $data[3]['annotation'];
|
||||
$array2 = $a2->toArray();
|
||||
$this->assertFalse($a2->isResource());
|
||||
$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());
|
||||
}
|
||||
|
||||
public function testGet()
|
||||
{
|
||||
$container = $this->getContainer();
|
||||
$extractor = $container->get('nelmio_api_doc.extractor.api_doc_extractor');
|
||||
$data = $extractor->get('Nelmio\ApiDocBundle\Tests\Fixtures\Controller\TestController::indexAction', 'test_route_1');
|
||||
$container = $this->getContainer();
|
||||
$extractor = $container->get('nelmio_api_doc.extractor.api_doc_extractor');
|
||||
$annotation = $extractor->get('Nelmio\ApiDocBundle\Tests\Fixtures\Controller\TestController::indexAction', 'test_route_1');
|
||||
|
||||
$this->assertTrue(isset($data['route']));
|
||||
$this->assertTrue(isset($data['annotation']));
|
||||
$this->assertInstanceOf('Nelmio\ApiDocBundle\Annotation\ApiDoc', $annotation);
|
||||
|
||||
$a = $data['annotation'];
|
||||
$this->assertTrue($a->isResource());
|
||||
$this->assertEquals('index action', $a->getDescription());
|
||||
$this->assertTrue(is_array($a->getFilters()));
|
||||
$this->assertNull($a->getFormType());
|
||||
$this->assertTrue($annotation->isResource());
|
||||
$this->assertEquals('index action', $annotation->getDescription());
|
||||
|
||||
$data2 = $extractor->get('nemlio.test.controller:indexAction', 'test_service_route_1');
|
||||
$data2['route']->setDefault('_controller', $data['route']->getDefault('_controller'));
|
||||
$this->assertEquals($data, $data2);
|
||||
$array = $annotation->toArray();
|
||||
$this->assertTrue(is_array($array['filters']));
|
||||
$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()
|
||||
@ -134,14 +139,14 @@ class ApiDocExtractorTest extends WebTestCase
|
||||
|
||||
public function testGetWithDocComment()
|
||||
{
|
||||
$container = $this->getContainer();
|
||||
$extractor = $container->get('nelmio_api_doc.extractor.api_doc_extractor');
|
||||
$data = $extractor->get('Nelmio\ApiDocBundle\Tests\Fixtures\Controller\TestController::myCommentedAction', 'test_route_5');
|
||||
$container = $this->getContainer();
|
||||
$extractor = $container->get('nelmio_api_doc.extractor.api_doc_extractor');
|
||||
$annotation = $extractor->get('Nelmio\ApiDocBundle\Tests\Fixtures\Controller\TestController::myCommentedAction', 'test_route_5');
|
||||
|
||||
$this->assertNotNull($data);
|
||||
$this->assertNotNull($annotation);
|
||||
$this->assertEquals(
|
||||
"This method is useful to test if the getDocComment works.",
|
||||
$data['annotation']->getDescription()
|
||||
$annotation->getDescription()
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -11,6 +11,7 @@
|
||||
|
||||
namespace Nelmio\ApiDocBundle\Tests\Fixtures\Controller;
|
||||
|
||||
use FOS\RestBundle\Controller\Annotations\QueryParam;
|
||||
use Nelmio\ApiDocBundle\Annotation\ApiDoc;
|
||||
use Symfony\Component\HttpFoundation\Response;
|
||||
|
||||
@ -81,4 +82,12 @@ class TestController
|
||||
public function anotherPostAction()
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* @ApiDoc()
|
||||
* @QueryParam(name="page", requirements="\d+", default="1", description="Page of the overview.")
|
||||
*/
|
||||
public function zActionWithQueryParamAction()
|
||||
{
|
||||
}
|
||||
}
|
||||
|
@ -34,6 +34,12 @@ test_route_7:
|
||||
requirements:
|
||||
_method: POST
|
||||
|
||||
test_route_8:
|
||||
pattern: /z-action-with-query-param
|
||||
defaults: { _controller: NelmioApiDocTestBundle:Test:zActionWithQueryParam }
|
||||
requirements:
|
||||
_method: GET
|
||||
|
||||
test_service_route_1:
|
||||
pattern: /tests
|
||||
defaults: { _controller: nemlio.test.controller:indexAction, _format: json }
|
||||
|
@ -34,12 +34,12 @@ _index action_
|
||||
|
||||
a:
|
||||
|
||||
* dataType: integer
|
||||
* DataType: integer
|
||||
|
||||
b:
|
||||
|
||||
* dataType: string
|
||||
* arbitrary: ["arg1","arg2"]
|
||||
* DataType: string
|
||||
* Arbitrary: ["arg1","arg2"]
|
||||
|
||||
|
||||
### `GET` /tests ###
|
||||
@ -50,12 +50,12 @@ _index action_
|
||||
|
||||
a:
|
||||
|
||||
* dataType: integer
|
||||
* DataType: integer
|
||||
|
||||
b:
|
||||
|
||||
* dataType: string
|
||||
* arbitrary: ["arg1","arg2"]
|
||||
* DataType: string
|
||||
* Arbitrary: ["arg1","arg2"]
|
||||
|
||||
|
||||
### `POST` /tests ###
|
||||
@ -158,6 +158,17 @@ _This method is useful to test if the getDocComment works._
|
||||
**id**
|
||||
|
||||
- Requirement: \d+
|
||||
|
||||
|
||||
### `GET` /z-action-with-query-param ###
|
||||
|
||||
|
||||
#### Filters ####
|
||||
|
||||
page:
|
||||
|
||||
* Requirement: \d+
|
||||
* Description: Page of the overview.
|
||||
MARKDOWN;
|
||||
|
||||
$this->assertEquals($expected, $result);
|
||||
@ -167,9 +178,9 @@ MARKDOWN;
|
||||
{
|
||||
$container = $this->getContainer();
|
||||
|
||||
$extractor = $container->get('nelmio_api_doc.extractor.api_doc_extractor');
|
||||
$data = $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']);
|
||||
$extractor = $container->get('nelmio_api_doc.extractor.api_doc_extractor');
|
||||
$annotation = $extractor->get('Nelmio\ApiDocBundle\Tests\Fixtures\Controller\TestController::indexAction', 'test_route_1');
|
||||
$result = $container->get('nelmio_api_doc.formatter.markdown_formatter')->formatOne($annotation);
|
||||
|
||||
$expected = <<<MARKDOWN
|
||||
### `GET` /tests ###
|
||||
@ -180,12 +191,12 @@ _index action_
|
||||
|
||||
a:
|
||||
|
||||
* dataType: integer
|
||||
* DataType: integer
|
||||
|
||||
b:
|
||||
|
||||
* dataType: string
|
||||
* arbitrary: ["arg1","arg2"]
|
||||
* DataType: string
|
||||
* Arbitrary: ["arg1","arg2"]
|
||||
|
||||
|
||||
MARKDOWN;
|
||||
|
@ -30,9 +30,6 @@ class SimpleFormatterTest extends WebTestCase
|
||||
array(
|
||||
'method' => 'GET',
|
||||
'uri' => '/tests',
|
||||
'requirements' =>
|
||||
array(
|
||||
),
|
||||
'filters' =>
|
||||
array(
|
||||
'a' =>
|
||||
@ -55,9 +52,6 @@ class SimpleFormatterTest extends WebTestCase
|
||||
array(
|
||||
'method' => 'GET',
|
||||
'uri' => '/tests',
|
||||
'requirements' =>
|
||||
array(
|
||||
),
|
||||
'filters' =>
|
||||
array(
|
||||
'a' =>
|
||||
@ -80,9 +74,6 @@ class SimpleFormatterTest extends WebTestCase
|
||||
array(
|
||||
'method' => 'POST',
|
||||
'uri' => '/tests',
|
||||
'requirements' =>
|
||||
array(
|
||||
),
|
||||
'parameters' =>
|
||||
array(
|
||||
'a' =>
|
||||
@ -110,9 +101,6 @@ class SimpleFormatterTest extends WebTestCase
|
||||
array(
|
||||
'method' => 'POST',
|
||||
'uri' => '/tests',
|
||||
'requirements' =>
|
||||
array(
|
||||
),
|
||||
'parameters' =>
|
||||
array(
|
||||
'a' =>
|
||||
@ -143,9 +131,6 @@ class SimpleFormatterTest extends WebTestCase
|
||||
array(
|
||||
'method' => 'POST',
|
||||
'uri' => '/another-post',
|
||||
'requirements' =>
|
||||
array(
|
||||
),
|
||||
'parameters' =>
|
||||
array(
|
||||
'a' =>
|
||||
@ -161,9 +146,6 @@ class SimpleFormatterTest extends WebTestCase
|
||||
array(
|
||||
'method' => 'ANY',
|
||||
'uri' => '/any',
|
||||
'requirements' =>
|
||||
array(
|
||||
),
|
||||
'description' => 'Action without HTTP verb',
|
||||
),
|
||||
2 =>
|
||||
@ -197,6 +179,15 @@ class SimpleFormatterTest extends WebTestCase
|
||||
'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+')
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
@ -207,14 +198,13 @@ class SimpleFormatterTest extends WebTestCase
|
||||
{
|
||||
$container = $this->getContainer();
|
||||
|
||||
$extractor = $container->get('nelmio_api_doc.extractor.api_doc_extractor');
|
||||
$data = $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']);
|
||||
$extractor = $container->get('nelmio_api_doc.extractor.api_doc_extractor');
|
||||
$annotation = $extractor->get('Nelmio\ApiDocBundle\Tests\Fixtures\Controller\TestController::indexAction', 'test_route_1');
|
||||
$result = $container->get('nelmio_api_doc.formatter.simple_formatter')->formatOne($annotation);
|
||||
|
||||
$expected = array(
|
||||
'method' => 'GET',
|
||||
'uri' => '/tests',
|
||||
'requirements' => array(),
|
||||
'filters' => array(
|
||||
'a' => array(
|
||||
'dataType' => 'integer',
|
||||
|
Loading…
x
Reference in New Issue
Block a user