diff --git a/DependencyInjection/NelmioApiDocExtension.php b/DependencyInjection/NelmioApiDocExtension.php index ce10263..9c04b28 100644 --- a/DependencyInjection/NelmioApiDocExtension.php +++ b/DependencyInjection/NelmioApiDocExtension.php @@ -16,6 +16,7 @@ use Symfony\Component\DependencyInjection\Loader\XmlFileLoader; use Symfony\Component\DependencyInjection\ContainerBuilder; use Symfony\Component\Config\Definition\Processor; use Symfony\Component\Config\FileLocator; +use Symfony\Component\DependencyInjection\Reference; class NelmioApiDocExtension extends Extension { @@ -48,5 +49,18 @@ class NelmioApiDocExtension extends Extension if (isset($config['sandbox']['authentication'])) { $container->setParameter('nelmio_api_doc.sandbox.authentication', $config['sandbox']['authentication']); } + + // Adding handlers from tagged services + $definition = $container->getDefinition( + 'nelmio_api_doc.extractor.api_doc_extractor' + ); + $taggedServices = $container->findTaggedServiceIds( + 'nelmio_api_doc.extractor.handler' + ); + $handlers = array(); + foreach ($taggedServices as $id => $attributes) { + $handlers[] = new Reference($id); + } + $definition->replaceArgument(4, $handlers); } } diff --git a/Extractor/ApiDocExtractor.php b/Extractor/ApiDocExtractor.php index e338fe9..6682e39 100644 --- a/Extractor/ApiDocExtractor.php +++ b/Extractor/ApiDocExtractor.php @@ -19,19 +19,12 @@ use Symfony\Component\Routing\RouterInterface; use Symfony\Component\DependencyInjection\ContainerInterface; use Symfony\Component\HttpFoundation\Request; use Nelmio\ApiDocBundle\Util\DocCommentExtractor; +use Nelmio\ApiDocBundle\Extractor\Handler\HandlerInterface; class ApiDocExtractor { const ANNOTATION_CLASS = 'Nelmio\\ApiDocBundle\\Annotation\\ApiDoc'; - const FOS_REST_QUERY_PARAM_CLASS = 'FOS\\RestBundle\\Controller\\Annotations\\QueryParam'; - - const FOS_REST_REQUEST_PARAM_CLASS = 'FOS\\RestBundle\\Controller\\Annotations\\RequestParam'; - - const JMS_SECURITY_EXTRA_SECURE_CLASS = 'JMS\\SecurityExtraBundle\\Annotation\\Secure'; - - const CACHE_ANNOTATION_CLASS = 'Sensio\\Bundle\\FrameworkExtraBundle\\Configuration\\Cache'; - /** * @var ContainerInterface */ @@ -57,12 +50,18 @@ class ApiDocExtractor */ protected $parsers = array(); - public function __construct(ContainerInterface $container, RouterInterface $router, Reader $reader, DocCommentExtractor $commentExtractor) + /** + * @var array HandlerInterface + */ + protected $handlers; + + public function __construct(ContainerInterface $container, RouterInterface $router, Reader $reader, DocCommentExtractor $commentExtractor, $handlers) { $this->container = $container; $this->router = $router; $this->reader = $reader; $this->commentExtractor = $commentExtractor; + $this->handlers = $handlers; } /** @@ -359,32 +358,9 @@ class ApiDocExtractor */ protected function parseAnnotations(ApiDoc $annotation, Route $route, \ReflectionMethod $method) { - foreach ($this->reader->getMethodAnnotations($method) as $annot) { - if (is_a($annot, self::FOS_REST_QUERY_PARAM_CLASS)) { - if ($annot->strict && $annot->default === null) { - $annotation->addRequirement($annot->name, array( - 'requirement' => $annot->requirements, - 'dataType' => '', - 'description' => $annot->description, - )); - } else { - $annotation->addFilter($annot->name, array( - 'requirement' => $annot->requirements, - 'description' => $annot->description, - )); - } - } elseif (is_a($annot, self::FOS_REST_REQUEST_PARAM_CLASS)) { - $annotation->addParameter($annot->name, array( - 'required' => $annot->strict && $annot->default === null, - 'dataType' => $annot->requirements, - 'description' => $annot->description, - 'readonly' => false - )); - } elseif (is_a($annot, self::JMS_SECURITY_EXTRA_SECURE_CLASS)) { - $annotation->setAuthentication(true); - } elseif (is_a($annot, self::CACHE_ANNOTATION_CLASS)) { - $annotation->setCache($annot->getMaxAge()); - } + $annots = $this->reader->getMethodAnnotations($method); + foreach ($this->handlers as $handler) { + $handler->handle($annotation, $annots); } } } diff --git a/Extractor/Handler/EmptyHandler.php b/Extractor/Handler/EmptyHandler.php new file mode 100644 index 0000000..1b1f958 --- /dev/null +++ b/Extractor/Handler/EmptyHandler.php @@ -0,0 +1,22 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Nelmio\ApiDocBundle\Extractor\Handler; + +use Nelmio\ApiDocBundle\Extractor\HandlerInterface; +use \Nelmio\ApiDocBundle\Annotation\ApiDoc; + +class EmptyHandler implements HandlerInterface +{ + public function handle(ApiDoc $annotation, $annotations) + { + } +} diff --git a/Extractor/Handler/FosRestQueryParamHandler.php b/Extractor/Handler/FosRestQueryParamHandler.php new file mode 100644 index 0000000..bcbf9db --- /dev/null +++ b/Extractor/Handler/FosRestQueryParamHandler.php @@ -0,0 +1,40 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Nelmio\ApiDocBundle\Extractor\Handler; + +use Nelmio\ApiDocBundle\Extractor\HandlerInterface; +use \Nelmio\ApiDocBundle\Annotation\ApiDoc; + +class FosRestQueryParamHandler implements HandlerInterface +{ + const FOS_REST_QUERY_PARAM_CLASS = 'FOS\\RestBundle\\Controller\\Annotations\\QueryParam'; + + public function handle(ApiDoc $annotation, $annotations) + { + foreach ($annotations as $annot) { + if (is_a($annot, self::FOS_REST_QUERY_PARAM_CLASS)) { + if ($annot->strict && $annot->default === null) { + $annotation->addRequirement($annot->name, array( + 'requirement' => $annot->requirements, + 'dataType' => '', + 'description' => $annot->description, + )); + } else { + $annotation->addFilter($annot->name, array( + 'requirement' => $annot->requirements, + 'description' => $annot->description, + )); + } + } + } + } +} diff --git a/Extractor/Handler/FosRestRequestParamHandler.php b/Extractor/Handler/FosRestRequestParamHandler.php new file mode 100644 index 0000000..1260bd4 --- /dev/null +++ b/Extractor/Handler/FosRestRequestParamHandler.php @@ -0,0 +1,34 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Nelmio\ApiDocBundle\Extractor\Handler; + +use Nelmio\ApiDocBundle\Extractor\HandlerInterface; +use \Nelmio\ApiDocBundle\Annotation\ApiDoc; + +class FosRestRequestParamHandler implements HandlerInterface +{ + const FOS_REST_REQUEST_PARAM_CLASS = 'FOS\\RestBundle\\Controller\\Annotations\\RequestParam'; + + public function handle(ApiDoc $annotation, $annotations) + { + foreach ($annotations as $annot) { + if (is_a($annot, self::FOS_REST_REQUEST_PARAM_CLASS)) { + $annotation->addParameter($annot->name, array( + 'required' => $annot->strict && $annot->default === null, + 'dataType' => $annot->requirements, + 'description' => $annot->description, + 'readonly' => false + )); + } + } + } +} diff --git a/Extractor/Handler/JmsSecurityExtraSecureHandler.php b/Extractor/Handler/JmsSecurityExtraSecureHandler.php new file mode 100644 index 0000000..1110ee1 --- /dev/null +++ b/Extractor/Handler/JmsSecurityExtraSecureHandler.php @@ -0,0 +1,29 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Nelmio\ApiDocBundle\Extractor\Handler; + +use Nelmio\ApiDocBundle\Extractor\HandlerInterface; +use \Nelmio\ApiDocBundle\Annotation\ApiDoc; + +class JmsSecurityExtraSecureHandler implements HandlerInterface +{ + const JMS_SECURITY_EXTRA_SECURE_CLASS = 'JMS\\SecurityExtraBundle\\Annotation\\Secure'; + + public function handle(ApiDoc $annotation, $annotations) + { + foreach ($annotations as $annot) { + if (is_a($annot, self::JMS_SECURITY_EXTRA_SECURE_CLASS)) { + $annotation->setAuthentication(true); + } + } + } +} diff --git a/Extractor/Handler/SensioFrameworkExtraCacheHandler.php b/Extractor/Handler/SensioFrameworkExtraCacheHandler.php new file mode 100644 index 0000000..1b22d86 --- /dev/null +++ b/Extractor/Handler/SensioFrameworkExtraCacheHandler.php @@ -0,0 +1,29 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Nelmio\ApiDocBundle\Extractor\Handler; + +use Nelmio\ApiDocBundle\Extractor\HandlerInterface; +use \Nelmio\ApiDocBundle\Annotation\ApiDoc; + +class SensioFrameworkExtraCacheHandler implements HandlerInterface +{ + const CACHE_ANNOTATION_CLASS = 'Sensio\\Bundle\\FrameworkExtraBundle\\Configuration\\Cache'; + + public function handle(ApiDoc $annotation, $annotations) + { + foreach ($annotations as $annot) { + if (is_a($annot, self::CACHE_ANNOTATION_CLASS)) { + $annotation->setCache($annot->getMaxAge()); + } + } + } +} diff --git a/Extractor/HandlerInterface.php b/Extractor/HandlerInterface.php new file mode 100644 index 0000000..bedf2ff --- /dev/null +++ b/Extractor/HandlerInterface.php @@ -0,0 +1,19 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Nelmio\ApiDocBundle\Extractor; + +use Nelmio\ApiDocBundle\Annotation\ApiDoc; + +interface HandlerInterface +{ + public function handle(ApiDoc $annotation, $annotations); +} diff --git a/README.md b/README.md index 0668cbb..a146aa2 100644 --- a/README.md +++ b/README.md @@ -236,6 +236,20 @@ You can also define your own motd content (above methods list). All you have to motd: template: AcmeApiBundle::Components/motd.html.twig +## Using your own annotations ## + +If you have developped your own project-related annotations, and you want to parse them to populate the ApiDoc, you can provide custom handlers as services. You juste have to implements the `Nelmio\ApiDocBundle\Extractor\HandlerInterface` and tag it as `nelmio_api_doc.extractor.handler`. + + #app/config/config.yml + services: + mybundle.api_doc.extractor.my_annotation_handler: + class: MyBundle\AnnotationHandler\MyAnnotationHandler; + tags: + - {name: nelmio_api_doc.extractor.handler} + +Look at examples in [Handlers](https://github.com/nelmio/NelmioApiDocBundle/tree/annotation_handlers/Extractor/Handler) + + ## Credits ## The design is heavily inspired by the [swagger-ui](https://github.com/wordnik/swagger-ui) project. diff --git a/Resources/config/services.xml b/Resources/config/services.xml index c1f9f7e..c53998c 100644 --- a/Resources/config/services.xml +++ b/Resources/config/services.xml @@ -8,6 +8,11 @@ Nelmio\ApiDocBundle\Form\Extension\DescriptionFormTypeExtension Nelmio\ApiDocBundle\Twig\Extension\MarkdownExtension Nelmio\ApiDocBundle\Util\DocCommentExtractor + + Nelmio\ApiDocBundle\Extractor\Handler\FosRestQueryParamHandler + Nelmio\ApiDocBundle\Extractor\Handler\FosRestRequestParamHandler + Nelmio\ApiDocBundle\Extractor\Handler\JmsSecurityExtraSecureHandler + Nelmio\ApiDocBundle\Extractor\Handler\SensioFrameworkExtraCacheHandler @@ -18,6 +23,7 @@ + @@ -27,6 +33,25 @@ + + + + + + + + + + + + + + + + + + +