mirror of
https://github.com/retailcrm/NelmioApiDocBundle.git
synced 2025-02-02 15:51:48 +03:00
Merge branch 'Nyholm-issue-404'
This commit is contained in:
commit
f601dc17fb
@ -25,6 +25,13 @@ class ApiDoc
|
||||
*/
|
||||
private $requirements = array();
|
||||
|
||||
/**
|
||||
* Which APIs is this route used. Defaults to "default"
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
private $apis = array();
|
||||
|
||||
/**
|
||||
* Filters are optional parameters in the query string.
|
||||
*
|
||||
@ -191,6 +198,15 @@ class ApiDoc
|
||||
}
|
||||
}
|
||||
|
||||
if (isset($data['api'])) {
|
||||
if (! is_array($data['api'])) {
|
||||
$data['api'] = array($data['api']);
|
||||
}
|
||||
foreach ($data['api'] as $api) {
|
||||
$this->addApi($api);
|
||||
}
|
||||
}
|
||||
|
||||
if (isset($data['parameters'])) {
|
||||
foreach ($data['parameters'] as $parameter) {
|
||||
if (!isset($parameter['name'])) {
|
||||
@ -373,6 +389,23 @@ class ApiDoc
|
||||
return $this->section;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
public function addApi($api)
|
||||
{
|
||||
$this->apis[] = $api;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
public function getApis()
|
||||
{
|
||||
return $this->apis;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @param string $documentation
|
||||
*/
|
||||
@ -625,6 +658,11 @@ class ApiDoc
|
||||
$data['requirements'] = $requirements;
|
||||
}
|
||||
|
||||
if ($apis = $this->apis) {
|
||||
$data['apis'] = $apis;
|
||||
}
|
||||
|
||||
|
||||
if ($response = $this->response) {
|
||||
$data['response'] = $response;
|
||||
}
|
||||
|
@ -19,9 +19,9 @@ use Symfony\Component\HttpFoundation\Response;
|
||||
|
||||
class ApiDocController extends Controller
|
||||
{
|
||||
public function indexAction()
|
||||
public function indexAction($api = "default")
|
||||
{
|
||||
$extractedDoc = $this->get('nelmio_api_doc.extractor.api_doc_extractor')->all();
|
||||
$extractedDoc = $this->get('nelmio_api_doc.extractor.api_doc_extractor')->all($api);
|
||||
$htmlContent = $this->get('nelmio_api_doc.formatter.html_formatter')->format($extractedDoc);
|
||||
|
||||
return new Response($htmlContent, 200, array('Content-Type' => 'text/html'));
|
||||
|
@ -96,9 +96,9 @@ class ApiDocExtractor
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function all()
|
||||
public function all($api = "default")
|
||||
{
|
||||
return $this->extractAnnotations($this->getRoutes());
|
||||
return $this->extractAnnotations($this->getRoutes(), $api);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -110,7 +110,7 @@ class ApiDocExtractor
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function extractAnnotations(array $routes)
|
||||
public function extractAnnotations(array $routes, $api = "default")
|
||||
{
|
||||
$array = array();
|
||||
$resources = array();
|
||||
@ -123,7 +123,10 @@ class ApiDocExtractor
|
||||
|
||||
if ($method = $this->getReflectionMethod($route->getDefault('_controller'))) {
|
||||
$annotation = $this->reader->getMethodAnnotation($method, self::ANNOTATION_CLASS);
|
||||
if ($annotation && !in_array($annotation->getSection(), $excludeSections)) {
|
||||
if ($annotation &&
|
||||
! in_array($annotation->getSection(), $excludeSections) &&
|
||||
( in_array($api, $annotation->getApis()) || (count($annotation->getApis()) == 0 && $api == "default"))
|
||||
) {
|
||||
if ($annotation->isResource()) {
|
||||
if ($resource = $annotation->getResource()) {
|
||||
$resources[] = $resource;
|
||||
|
@ -49,7 +49,7 @@ class CachingApiDocExtractor extends ApiDocExtractor
|
||||
$this->cache = new ConfigCache($this->cacheFile, $debug);
|
||||
}
|
||||
|
||||
public function all()
|
||||
public function all($api = "default")
|
||||
{
|
||||
if ($this->cache->isFresh() === false) {
|
||||
|
||||
@ -65,7 +65,7 @@ class CachingApiDocExtractor extends ApiDocExtractor
|
||||
|
||||
$resources = array_merge($resources, $this->router->getRouteCollection()->getResources());
|
||||
|
||||
$data = parent::all();
|
||||
$data = parent::all($api);
|
||||
$this->cache->write(serialize($data), $resources);
|
||||
|
||||
return $data;
|
||||
|
@ -1,5 +1,5 @@
|
||||
nelmio_api_doc_index:
|
||||
pattern: /
|
||||
defaults: { _controller: NelmioApiDocBundle:ApiDoc:index }
|
||||
pattern: /{api}
|
||||
defaults: { _controller: NelmioApiDocBundle:ApiDoc:index, api: "default" }
|
||||
requirements:
|
||||
_method: GET
|
||||
|
@ -188,6 +188,9 @@ class YourController
|
||||
}
|
||||
```
|
||||
|
||||
* `api`: the api under which this resource will be shown. Leave empty to specify the default api. Either a single api, or
|
||||
an array of apis.
|
||||
|
||||
Each _filter_ has to define a `name` parameter, but other parameters are free. Filters are often optional
|
||||
parameters, and you can document them as you want, but keep in mind to be consistent for the whole documentation.
|
||||
|
||||
@ -220,6 +223,56 @@ class YourType extends AbstractType
|
||||
The bundle will also get information from the routing definition (`requirements`, `pattern`, etc), so to get the
|
||||
best out of it you should define strict _method requirements etc.
|
||||
|
||||
### Multiple API documentations ###
|
||||
With the `api` tag in the `@apidoc` annotation, it's possible to create different sets of api documentations. Without
|
||||
the tag, all methods are located in the `default` api and can be found under the normal api documentation url. With the
|
||||
`api` tag you can specify one or more api names under which the method will be visible.
|
||||
|
||||
An example:
|
||||
```
|
||||
/**
|
||||
* A resource
|
||||
*
|
||||
* @ApiDoc(
|
||||
* resource=true,
|
||||
* description="This is a description of your API method",
|
||||
* api = { "default", "premium" }
|
||||
* )
|
||||
*/
|
||||
public function getAction()
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* Another resource
|
||||
*
|
||||
* @ApiDoc(
|
||||
* resource=true,
|
||||
* description="This is a description of another API method",
|
||||
* api = { "premium" }
|
||||
* )
|
||||
*/
|
||||
public function getAnotherAction()
|
||||
{
|
||||
}
|
||||
```
|
||||
|
||||
In this case, only the first resource will be available under the default api documentation, while both methods will
|
||||
be available under the `premium` api documentation.
|
||||
|
||||
#### Accessing API documentation ####
|
||||
The normal `default` documentation can be found at the normal location. Other sets of documentation can be found at `documentationurl/<tagname>`.
|
||||
|
||||
For instance, if your documenation is located at
|
||||
|
||||
http://example.org/doc/api/v1/
|
||||
|
||||
then the `premium` api will be located at:
|
||||
|
||||
http://example.org/doc/api/v1/premium
|
||||
|
||||
|
||||
|
||||
### Other Bundle Annotations
|
||||
|
||||
Also bundle will get information from the other annotations:
|
||||
@ -464,6 +517,11 @@ You can specify which sections to exclude from the documentation generation:
|
||||
nelmio_api_doc:
|
||||
exclude_sections: ["privateapi", "testapi"]
|
||||
```
|
||||
|
||||
Note that `exclude_sections` will literally exclude a section from your api documentation. It's possible however to create
|
||||
multiple apis by specifying the `api` within the `@apidoc` annotations. This allows you to move private or test methods to a
|
||||
complete different set of api documentation instead.
|
||||
|
||||
The bundle provides a way to register multiple `input` parsers. The first parser
|
||||
that can handle the specified input is used, so you can configure their
|
||||
priorities via container tags. Here's an example parser service registration:
|
||||
@ -492,6 +550,7 @@ nelmio_api_doc:
|
||||
enabled: true
|
||||
file: "/tmp/symfony-app/%kernel.environment%/api-doc.cache"
|
||||
```
|
||||
|
||||
### Using Your Own Annotations
|
||||
|
||||
If you have developed your own project-related annotations, and you want to parse them to populate
|
||||
|
@ -26,6 +26,7 @@ class ApiDocTest extends TestCase
|
||||
$this->assertTrue(is_array($array));
|
||||
$this->assertFalse(isset($array['filters']));
|
||||
$this->assertFalse($annot->isResource());
|
||||
$this->assertEmpty($annot->getApis());
|
||||
$this->assertFalse($annot->getDeprecated());
|
||||
$this->assertFalse(isset($array['description']));
|
||||
$this->assertFalse(isset($array['requirements']));
|
||||
|
@ -17,6 +17,10 @@ use Nelmio\ApiDocBundle\Tests\WebTestCase;
|
||||
|
||||
class ApiDocExtractorTest extends WebTestCase
|
||||
{
|
||||
const ROUTES_QUANTITY_DEFAULT = 33; // Routes in the default api
|
||||
const ROUTES_QUANTITY_PREMIUM = 6; // Routes tagged with premium api
|
||||
const ROUTES_QUANTITY_TEST = 2; // Routes tagged with test api
|
||||
|
||||
public function testAll()
|
||||
{
|
||||
$container = $this->getContainer();
|
||||
@ -29,7 +33,7 @@ class ApiDocExtractorTest extends WebTestCase
|
||||
$routesQuantity = 38;
|
||||
$httpsKey = 25;
|
||||
} else {
|
||||
$routesQuantity = 33;
|
||||
$routesQuantity = self::ROUTES_QUANTITY_DEFAULT;
|
||||
$httpsKey = 20;
|
||||
}
|
||||
|
||||
@ -287,4 +291,76 @@ class ApiDocExtractorTest extends WebTestCase
|
||||
$parameters = $annotation->getParameters();
|
||||
$this->assertFalse($parameters['required_field']['required']);
|
||||
}
|
||||
|
||||
public function multiDocProvider()
|
||||
{
|
||||
return array(
|
||||
array('default', self::ROUTES_QUANTITY_DEFAULT),
|
||||
array('premium', self::ROUTES_QUANTITY_PREMIUM),
|
||||
array('test', self::ROUTES_QUANTITY_TEST),
|
||||
array('foobar', 0),
|
||||
array("", 0),
|
||||
array(null, 0),
|
||||
);
|
||||
}
|
||||
|
||||
public function testAllMultiDocsForTest()
|
||||
{
|
||||
$container = $this->getContainer();
|
||||
$extractor = $container->get('nelmio_api_doc.extractor.api_doc_extractor');
|
||||
set_error_handler(array($this, 'handleDeprecation'));
|
||||
$data = $extractor->all('test');
|
||||
restore_error_handler();
|
||||
|
||||
$this->assertTrue(is_array($data));
|
||||
$this->assertCount(self::ROUTES_QUANTITY_TEST, $data);
|
||||
|
||||
$a1 = $data[0]['annotation'];
|
||||
$this->assertCount(3, $a1->getApis());
|
||||
$this->assertEquals('List resources.', $a1->getDescription());
|
||||
|
||||
$a2 = $data[1]['annotation'];
|
||||
$this->assertCount(2, $a2->getApis());
|
||||
$this->assertEquals('create another test', $a2->getDescription());
|
||||
}
|
||||
|
||||
public function testAllMultiDocsForPremium()
|
||||
{
|
||||
$container = $this->getContainer();
|
||||
$extractor = $container->get('nelmio_api_doc.extractor.api_doc_extractor');
|
||||
set_error_handler(array($this, 'handleDeprecation'));
|
||||
$data = $extractor->all('premium');
|
||||
restore_error_handler();
|
||||
|
||||
$this->assertTrue(is_array($data));
|
||||
$this->assertCount(self::ROUTES_QUANTITY_PREMIUM, $data);
|
||||
|
||||
$a1 = $data[0]['annotation'];
|
||||
$this->assertCount(2, $a1->getApis());
|
||||
$this->assertEquals('List another resource.', $a1->getDescription());
|
||||
|
||||
$a2 = $data[1]['annotation'];
|
||||
$this->assertCount(3, $a2->getApis());
|
||||
$this->assertEquals('List resources.', $a2->getDescription());
|
||||
|
||||
$a3 = $data[4]['annotation'];
|
||||
$this->assertCount(2, $a3->getApis());
|
||||
$this->assertEquals('create test', $a3->getDescription());
|
||||
}
|
||||
|
||||
/**
|
||||
* @dataProvider multiDocProvider
|
||||
*/
|
||||
public function testAllMultiDocs($api, $count)
|
||||
{
|
||||
$container = $this->getContainer();
|
||||
$extractor = $container->get('nelmio_api_doc.extractor.api_doc_extractor');
|
||||
set_error_handler(array($this, 'handleDeprecation'));
|
||||
$data = $extractor->all($api);
|
||||
restore_error_handler();
|
||||
|
||||
$this->assertTrue(is_array($data));
|
||||
$this->assertCount($count, $data);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -17,6 +17,7 @@ class ResourceController
|
||||
/**
|
||||
* @ApiDoc(
|
||||
* resource=true,
|
||||
* api={ "test", "premium", "default" },
|
||||
* resourceDescription="Operations on resource.",
|
||||
* description="List resources.",
|
||||
* output="array<Nelmio\ApiDocBundle\Tests\Fixtures\Model\Test> as tests",
|
||||
@ -47,6 +48,7 @@ class ResourceController
|
||||
/**
|
||||
* @ApiDoc(
|
||||
* description="Create a new resource.",
|
||||
* api={ "default", "premium" },
|
||||
* input={"class" = "Nelmio\ApiDocBundle\Tests\Fixtures\Form\SimpleType", "name" = ""},
|
||||
* output="Nelmio\ApiDocBundle\Tests\Fixtures\Model\JmsNested",
|
||||
* responseMap={
|
||||
@ -62,6 +64,7 @@ class ResourceController
|
||||
/**
|
||||
* @ApiDoc(
|
||||
* resource=true,
|
||||
* api={ "default", "premium" },
|
||||
* description="List another resource.",
|
||||
* resourceDescription="Operations on another resource.",
|
||||
* output="array<Nelmio\ApiDocBundle\Tests\Fixtures\Model\JmsTest>"
|
||||
|
@ -23,7 +23,8 @@ class TestController
|
||||
{
|
||||
/**
|
||||
* @ApiDoc(
|
||||
* resource="TestResource"
|
||||
* resource="TestResource",
|
||||
* api="default"
|
||||
* )
|
||||
*/
|
||||
public function namedResourceAction()
|
||||
@ -48,6 +49,7 @@ class TestController
|
||||
/**
|
||||
* @ApiDoc(
|
||||
* description="create test",
|
||||
* api={ "default", "premium" },
|
||||
* input="Nelmio\ApiDocBundle\Tests\Fixtures\Form\TestType"
|
||||
* )
|
||||
*/
|
||||
@ -58,6 +60,7 @@ class TestController
|
||||
/**
|
||||
* @ApiDoc(
|
||||
* description="post test 2",
|
||||
* api={ "default", "premium" },
|
||||
* resource=true
|
||||
* )
|
||||
*/
|
||||
@ -109,6 +112,7 @@ class TestController
|
||||
|
||||
/**
|
||||
* @ApiDoc(
|
||||
* api= { "default", "test" },
|
||||
* description="create another test",
|
||||
* input="dependency_type"
|
||||
* )
|
||||
|
@ -2557,6 +2557,11 @@ With multiple lines.',
|
||||
'authentication' => false,
|
||||
'authenticationRoles' => array(),
|
||||
'deprecated' => false,
|
||||
'apis' =>
|
||||
array(
|
||||
'default',
|
||||
'premium',
|
||||
),
|
||||
),
|
||||
3 =>
|
||||
array(
|
||||
@ -2616,6 +2621,11 @@ With multiple lines.',
|
||||
'authentication' => false,
|
||||
'authenticationRoles' => array(),
|
||||
'deprecated' => false,
|
||||
'apis' =>
|
||||
array(
|
||||
'default',
|
||||
'premium',
|
||||
),
|
||||
),
|
||||
),
|
||||
'others' =>
|
||||
@ -2652,6 +2662,11 @@ With multiple lines.',
|
||||
'authentication' => false,
|
||||
'authenticationRoles' => array(),
|
||||
'deprecated' => false,
|
||||
'apis' =>
|
||||
array(
|
||||
'default',
|
||||
'test',
|
||||
),
|
||||
),
|
||||
1 =>
|
||||
array(
|
||||
@ -3676,6 +3691,11 @@ With multiple lines.',
|
||||
'authentication' => false,
|
||||
'authenticationRoles' => array(),
|
||||
'deprecated' => false,
|
||||
'apis' =>
|
||||
array(
|
||||
'default',
|
||||
'premium',
|
||||
),
|
||||
),
|
||||
),
|
||||
'/tests2' =>
|
||||
@ -3697,6 +3717,11 @@ With multiple lines.',
|
||||
'authentication' => false,
|
||||
'authenticationRoles' => array(),
|
||||
'deprecated' => false,
|
||||
'apis' =>
|
||||
array(
|
||||
'default',
|
||||
'premium',
|
||||
),
|
||||
),
|
||||
),
|
||||
'TestResource' =>
|
||||
@ -3709,6 +3734,10 @@ With multiple lines.',
|
||||
'authentication' => false,
|
||||
'authenticationRoles' => array(),
|
||||
'deprecated' => false,
|
||||
'apis' =>
|
||||
array(
|
||||
'default',
|
||||
),
|
||||
),
|
||||
),
|
||||
'/api/other-resources' =>
|
||||
@ -3732,6 +3761,10 @@ With multiple lines.',
|
||||
'authenticationRoles' =>
|
||||
array(),
|
||||
'deprecated' => false,
|
||||
'apis' => array(
|
||||
'default',
|
||||
'premium',
|
||||
),
|
||||
'response' => array(
|
||||
'' =>
|
||||
array(
|
||||
@ -4054,6 +4087,11 @@ With multiple lines.',
|
||||
'authenticationRoles' =>
|
||||
array(),
|
||||
'deprecated' => false,
|
||||
'apis' => array(
|
||||
'test',
|
||||
'default',
|
||||
'premium',
|
||||
),
|
||||
'response' =>
|
||||
array(
|
||||
'tests' =>
|
||||
@ -4167,6 +4205,11 @@ With multiple lines.',
|
||||
'description' => '',
|
||||
),
|
||||
),
|
||||
'apis' =>
|
||||
array(
|
||||
'default',
|
||||
'premium',
|
||||
),
|
||||
'response' =>
|
||||
array(
|
||||
'foo' =>
|
||||
|
Loading…
x
Reference in New Issue
Block a user