From 74d30d9e396f481ce2751759e4e3281f4e733b32 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Konrad=20Podg=C3=B3rski?= Date: Wed, 17 Jul 2013 20:41:27 +0200 Subject: [PATCH] Option to set requirements and parameters directly from ApiDoc annotation Sometimes required parameters are not used through routing but still they are mandatory. I wanted to have API with resource.json?foo=bar&something=else format, that was possible through QueryParam annotation or requirements in routing BUT! There was no way to set dataType or description This PR solves problem for me. Side note: if you want to declare e.g. _format requirement through Annotation or any other param that is used in url ({foo} format) then it won't work. Because Bundle still overrides requirements and parameters after the constuctor in ApiDoc is called. This might be solved in separate PR by adding check if given requirements or parameters was already defined. --- Annotation/ApiDoc.php | 33 ++++++++++++ README.md | 24 +++++++++ Resources/views/method.html.twig | 4 +- Tests/Annotation/ApiDocTest.php | 55 ++++++++++++++++++++ Tests/Fixtures/Controller/TestController.php | 15 ++++++ 5 files changed, 129 insertions(+), 2 deletions(-) diff --git a/Annotation/ApiDoc.php b/Annotation/ApiDoc.php index 760e174..ab2a8f0 100644 --- a/Annotation/ApiDoc.php +++ b/Annotation/ApiDoc.php @@ -153,6 +153,39 @@ class ApiDoc } } + if (isset($data['requirements'])) { + foreach ($data['requirements'] as $requirement) { + if (!isset($requirement['name'])) { + throw new \InvalidArgumentException('A "requirement" element has to contain a "name" attribute'); + } + + $name = $requirement['name']; + unset($requirement['name']); + + $this->addRequirement($name, $requirement); + } + } + + if (isset($data['parameters'])) { + foreach ($data['parameters'] as $parameter) { + if (!isset($parameter['name'])) { + throw new \InvalidArgumentException('A "parameter" element has to contain a "name" attribute'); + } + + if (!isset($parameter['dataType'])) { + throw new \InvalidArgumentException(sprintf( + '"%s" parameter element has to contain a "dataType" attribute', + $parameter['name'] + )); + } + + $name = $parameter['name']; + unset($parameter['name']); + + $this->addParameter($name, $parameter); + } + } + if (isset($data['output'])) { $this->output = $data['output']; } diff --git a/README.md b/README.md index b78d912..48b44be 100644 --- a/README.md +++ b/README.md @@ -89,6 +89,26 @@ class YourController extends Controller public function postAction() { } + + /** + * @ApiDoc( + * description="Returns a collection of Object", + * requirements={ + * { + * "name"="limit", + * "dataType"="integer", + * "requirement"="\d+", + * "description"="how many objects to return" + * } + * }, + * parameters={ + * {"name"="categoryId", "dataType"="integer", "required"=true, "description"="category id"} + * } + * ) + */ + public function cgetAction($id) + { + } } ``` @@ -104,6 +124,10 @@ The following properties are available: * `filters`: an array of filters; +* `requirements`: an array of requirements; + +* `parameters`: an array of parameters; + * `input`: the input type associated to the method (currently this supports Form Types, classes with JMS Serializer metadata, and classes with Validation component metadata) useful for POST|PUT methods, either as FQCN or as form type (if it is registered in the form factory in the container). diff --git a/Resources/views/method.html.twig b/Resources/views/method.html.twig index c0917ec..eba8d20 100644 --- a/Resources/views/method.html.twig +++ b/Resources/views/method.html.twig @@ -123,10 +123,10 @@ {% if not infos.readonly %} {{ name }} - {{ infos.dataType }} + {{ infos.dataType is defined ? infos.dataType : '' }} {{ infos.required ? 'true' : 'false' }} {{ infos.format }} - {{ infos.description }} + {{ infos.description is defined ? infos.description : '' }} {% endif %} {% endfor %} diff --git a/Tests/Annotation/ApiDocTest.php b/Tests/Annotation/ApiDocTest.php index 3c7eb5f..e18fb1f 100644 --- a/Tests/Annotation/ApiDocTest.php +++ b/Tests/Annotation/ApiDocTest.php @@ -28,6 +28,8 @@ class ApiDocTest extends TestCase $this->assertFalse($annot->isResource()); $this->assertFalse($annot->getDeprecated()); $this->assertFalse(isset($array['description'])); + $this->assertFalse(isset($array['requirements'])); + $this->assertFalse(isset($array['parameters'])); $this->assertNull($annot->getInput()); $this->assertFalse($array['authentication']); } @@ -47,6 +49,8 @@ class ApiDocTest extends TestCase $this->assertFalse($annot->isResource()); $this->assertFalse($annot->getDeprecated()); $this->assertFalse(isset($array['description'])); + $this->assertFalse(isset($array['requirements'])); + $this->assertFalse(isset($array['parameters'])); $this->assertNull($annot->getInput()); } @@ -64,6 +68,8 @@ class ApiDocTest extends TestCase $this->assertFalse($annot->isResource()); $this->assertFalse($annot->getDeprecated()); $this->assertEquals($data['description'], $array['description']); + $this->assertFalse(isset($array['requirements'])); + $this->assertFalse(isset($array['parameters'])); $this->assertNull($annot->getInput()); } @@ -82,6 +88,8 @@ class ApiDocTest extends TestCase $this->assertFalse($annot->isResource()); $this->assertFalse($annot->getDeprecated()); $this->assertEquals($data['description'], $array['description']); + $this->assertFalse(isset($array['requirements'])); + $this->assertFalse(isset($array['parameters'])); $this->assertEquals($data['input'], $annot->getInput()); } @@ -102,6 +110,8 @@ class ApiDocTest extends TestCase $this->assertTrue($annot->isResource()); $this->assertTrue($annot->getDeprecated()); $this->assertEquals($data['description'], $array['description']); + $this->assertFalse(isset($array['requirements'])); + $this->assertFalse(isset($array['parameters'])); $this->assertEquals($data['input'], $annot->getInput()); } @@ -121,6 +131,8 @@ class ApiDocTest extends TestCase $this->assertFalse(isset($array['filters'])); $this->assertFalse($annot->isResource()); $this->assertEquals($data['description'], $array['description']); + $this->assertFalse(isset($array['requirements'])); + $this->assertFalse(isset($array['parameters'])); $this->assertEquals($data['deprecated'], $array['deprecated']); $this->assertEquals($data['input'], $annot->getInput()); } @@ -145,6 +157,8 @@ class ApiDocTest extends TestCase $this->assertEquals(array('a-filter' => array()), $array['filters']); $this->assertTrue($annot->isResource()); $this->assertEquals($data['description'], $array['description']); + $this->assertFalse(isset($array['requirements'])); + $this->assertFalse(isset($array['parameters'])); $this->assertEquals($data['deprecated'], $array['deprecated']); $this->assertNull($annot->getInput()); } @@ -232,4 +246,45 @@ class ApiDocTest extends TestCase $this->assertEquals($data['cache'], $array['cache']); } + + public function testConstructWithRequirements() + { + $data = array( + 'requirements' => array( + array( + 'name' => 'fooId', + 'requirement' => '\d+', + 'dataType' => 'integer', + 'description' => 'This requirement might be used withing action method directly from Request object' + ) + ) + ); + + $annot = new ApiDoc($data); + $array = $annot->toArray(); + + $this->assertTrue(is_array($array)); + $this->assertTrue(isset($array['requirements']['fooId'])); + $this->assertTrue(isset($array['requirements']['fooId']['dataType'])); + } + + public function testConstructWithParameters() + { + $data = array( + 'parameters' => array( + array( + 'name' => 'fooId', + 'dataType' => 'integer', + 'description' => 'Some description' + ) + ) + ); + + $annot = new ApiDoc($data); + $array = $annot->toArray(); + + $this->assertTrue(is_array($array)); + $this->assertTrue(isset($array['parameters']['fooId'])); + $this->assertTrue(isset($array['parameters']['fooId']['dataType'])); + } } diff --git a/Tests/Fixtures/Controller/TestController.php b/Tests/Fixtures/Controller/TestController.php index c9e1eae..a235b1d 100644 --- a/Tests/Fixtures/Controller/TestController.php +++ b/Tests/Fixtures/Controller/TestController.php @@ -198,4 +198,19 @@ class TestController public function jmsReturnNestedOutputAction() { } + + /** + * @ApiDoc( + * description="Returns a collection of Object", + * requirements={ + * {"name"="limit", "dataType"="integer", "requirement"="\d+", "description"="how many objects to return"} + * }, + * parameters={ + * {"name"="categoryId", "dataType"="integer", "required"=true, "description"="category id"} + * } + * ) + */ + public function cgetAction($id) + { + } }