diff --git a/ApiDocGenerator.php b/ApiDocGenerator.php index ac93116..a82140d 100644 --- a/ApiDocGenerator.php +++ b/ApiDocGenerator.php @@ -12,7 +12,7 @@ namespace EXSyst\Bundle\ApiDocBundle; use EXSyst\Bundle\ApiDocBundle\Describer\DescriberInterface; -use EXSyst\Swagger\Swagger; +use EXSyst\Component\Swagger\Swagger; class ApiDocGenerator { diff --git a/Describer/DefaultDescriber.php b/Describer/DefaultDescriber.php new file mode 100644 index 0000000..88a864e --- /dev/null +++ b/Describer/DefaultDescriber.php @@ -0,0 +1,64 @@ + + */ +class DefaultDescriber implements DescriberInterface +{ + public function describe(Swagger $api) + { + // Info + $info = $api->getInfo(); + if (null === $info->getTitle()) { + $info->setTitle(''); + } + if (null === $info->getVersion()) { + $info->setVersion('0.0.0'); + } + + // Paths + $paths = $api->getPaths(); + foreach ($paths as $uri => $path) { + // Path Parameters + preg_match_all('/\{(.+)\}/SU', $uri, $matches); + $pathParameters = $matches[1]; + + foreach ($path->getMethods() as $method) { + $operation = $path->getOperation($method); + $parameters = $operation->getParameters(); + + // Default Path Parameters + foreach ($pathParameters as $pathParameter) { + if ($parameters->has($pathParameter, 'path')) { + continue; + } + + $parameters->get($pathParameter, 'path') + ->setRequired(true) + ->setType('string'); + } + + // Default Response + if (0 === iterator_count($operation->getResponses())) { + $defaultResponse = $operation->getResponses()->get('default'); + $defaultResponse->setDescription(''); + } + } + } + } +} diff --git a/Describer/DescriberInterface.php b/Describer/DescriberInterface.php index a66fdaa..81f803a 100644 --- a/Describer/DescriberInterface.php +++ b/Describer/DescriberInterface.php @@ -11,7 +11,7 @@ namespace EXSyst\Bundle\ApiDocBundle\Describer; -use EXSyst\Swagger\Swagger; +use EXSyst\Component\Swagger\Swagger; interface DescriberInterface { diff --git a/Describer/ExternalDocDescriber.php b/Describer/ExternalDocDescriber.php index a7d83dd..d86d139 100644 --- a/Describer/ExternalDocDescriber.php +++ b/Describer/ExternalDocDescriber.php @@ -11,7 +11,7 @@ namespace EXSyst\Bundle\ApiDocBundle\Describer; -use EXSyst\Swagger\Swagger; +use EXSyst\Component\Swagger\Swagger; class ExternalDocDescriber implements DescriberInterface { diff --git a/Describer/RouteDescriber.php b/Describer/RouteDescriber.php index 3cd4a9c..f8e884e 100644 --- a/Describer/RouteDescriber.php +++ b/Describer/RouteDescriber.php @@ -13,7 +13,7 @@ namespace EXSyst\Bundle\ApiDocBundle\Describer; use Doctrine\Common\Util\ClassUtils; use EXSyst\Bundle\ApiDocBundle\RouteDescriber\RouteDescriberInterface; -use EXSyst\Swagger\Swagger; +use EXSyst\Component\Swagger\Swagger; use Symfony\Bundle\FrameworkBundle\Controller\ControllerNameParser; use Symfony\Component\DependencyInjection\ContainerInterface; use Symfony\Component\Routing\Route; diff --git a/Describer/SwaggerPhpDescriber.php b/Describer/SwaggerPhpDescriber.php index 4d89666..8354a80 100644 --- a/Describer/SwaggerPhpDescriber.php +++ b/Describer/SwaggerPhpDescriber.php @@ -16,9 +16,22 @@ class SwaggerPhpDescriber extends ExternalDocDescriber public function __construct(string $projectPath, bool $overwrite = false) { parent::__construct(function () use ($projectPath) { - $annotation = \Swagger\scan($projectPath); + // Catch notices as the documentation can be completed by other describers + $prevHandler = set_error_handler(function ($type, $message, $file, $line, $context) use (&$prevHandler) { + if (E_USER_NOTICE === $type || E_USER_WARNING === $type) { + return; + } - return json_decode(json_encode($annotation)); + return null !== $prevHandler && call_user_func($prevHandler, $type, $message, $file, $line, $context); + }); + + try { + $annotation = \Swagger\scan($projectPath); + + return json_decode(json_encode($annotation)); + } finally { + restore_error_handler(); + } }, $overwrite); } } diff --git a/Resources/config/services.xml b/Resources/config/services.xml index ebdb4ba..26f39f9 100644 --- a/Resources/config/services.xml +++ b/Resources/config/services.xml @@ -24,6 +24,10 @@ + + + + diff --git a/RouteDescriber/NelmioAnnotationDescriber.php b/RouteDescriber/NelmioAnnotationDescriber.php index e81bce6..2bc577c 100644 --- a/RouteDescriber/NelmioAnnotationDescriber.php +++ b/RouteDescriber/NelmioAnnotationDescriber.php @@ -12,8 +12,8 @@ namespace EXSyst\Bundle\ApiDocBundle\RouteDescriber; use Doctrine\Common\Annotations\Reader; -use EXSyst\Swagger\Parameter; -use EXSyst\Swagger\Swagger; +use EXSyst\Component\Swagger\Parameter; +use EXSyst\Component\Swagger\Swagger; use Nelmio\ApiDocBundle\Annotation\ApiDoc; use Symfony\Component\Routing\Route; diff --git a/RouteDescriber/PhpDocDescriber.php b/RouteDescriber/PhpDocDescriber.php index 9fe8165..7c73b53 100644 --- a/RouteDescriber/PhpDocDescriber.php +++ b/RouteDescriber/PhpDocDescriber.php @@ -11,7 +11,7 @@ namespace EXSyst\Bundle\ApiDocBundle\RouteDescriber; -use EXSyst\Swagger\Swagger; +use EXSyst\Component\Swagger\Swagger; use phpDocumentor\Reflection\DocBlockFactory; use phpDocumentor\Reflection\DocBlockFactoryInterface; use Symfony\Component\Routing\Route; @@ -48,10 +48,14 @@ class PhpDocDescriber implements RouteDescriberInterface if (null !== $docBlock) { $operation->setSummary($docBlock->getSummary()); $operation->setDescription((string) $docBlock->getDescription()); - $operation->setDeprecated($operation->getDeprecated() || $docBlock->hasTag('deprecated')); + if ($docBlock->hasTag('deprecated')) { + $operation->setDeprecated(true); + } } if (null !== $classDocBlock) { - $operation->setDeprecated($operation->getDeprecated() || $classDocBlock->hasTag('deprecated')); + if ($classDocBlock->hasTag('deprecated')) { + $operation->setDeprecated(true); + } } } } diff --git a/RouteDescriber/RouteDescriberInterface.php b/RouteDescriber/RouteDescriberInterface.php index dbdcfce..38fa81e 100644 --- a/RouteDescriber/RouteDescriberInterface.php +++ b/RouteDescriber/RouteDescriberInterface.php @@ -11,7 +11,7 @@ namespace EXSyst\Bundle\ApiDocBundle\RouteDescriber; -use EXSyst\Swagger\Swagger; +use EXSyst\Component\Swagger\Swagger; use Symfony\Component\Routing\Route; interface RouteDescriberInterface diff --git a/RouteDescriber/RouteDescriberTrait.php b/RouteDescriber/RouteDescriberTrait.php index 258517a..2e01844 100644 --- a/RouteDescriber/RouteDescriberTrait.php +++ b/RouteDescriber/RouteDescriberTrait.php @@ -11,8 +11,8 @@ namespace EXSyst\Bundle\ApiDocBundle\RouteDescriber; -use EXSyst\Swagger\Operation; -use EXSyst\Swagger\Swagger; +use EXSyst\Component\Swagger\Operation; +use EXSyst\Component\Swagger\Swagger; use Symfony\Component\Routing\Route; /** @@ -27,7 +27,7 @@ trait RouteDescriberTrait */ private function getOperations(Swagger $api, Route $route) { - $path = $api->getPaths()->get($route->getPath()); + $path = $api->getPaths()->get($this->normalizePath($route->getPath())); $methods = $route->getMethods() ?: Swagger::$METHODS; foreach ($methods as $method) { $method = strtolower($method); @@ -40,4 +40,13 @@ trait RouteDescriberTrait return $operations; } + + private function normalizePath(string $path) + { + if (substr($path, -10) === '.{_format}') { + $path = substr($path, 0, -10); + } + + return $path; + } } diff --git a/RouteDescriber/RouteMetadataDescriber.php b/RouteDescriber/RouteMetadataDescriber.php index 6228693..a32685b 100644 --- a/RouteDescriber/RouteMetadataDescriber.php +++ b/RouteDescriber/RouteMetadataDescriber.php @@ -11,7 +11,7 @@ namespace EXSyst\Bundle\ApiDocBundle\RouteDescriber; -use EXSyst\Swagger\Swagger; +use EXSyst\Component\Swagger\Swagger; use Symfony\Component\Routing\Route; class RouteMetadataDescriber implements RouteDescriberInterface diff --git a/Tests/Describer/AbstractDescriberTest.php b/Tests/Describer/AbstractDescriberTest.php new file mode 100644 index 0000000..861e973 --- /dev/null +++ b/Tests/Describer/AbstractDescriberTest.php @@ -0,0 +1,27 @@ +describer->describe($api); + + return $api; + } +} diff --git a/Tests/Describer/SwaggerPhpDescriberTest.php b/Tests/Describer/SwaggerPhpDescriberTest.php new file mode 100644 index 0000000..7bff009 --- /dev/null +++ b/Tests/Describer/SwaggerPhpDescriberTest.php @@ -0,0 +1,32 @@ +getSwaggerDoc(); + $info = $api->getInfo(); + + $this->assertEquals('My Awesome App', $info->getTitle()); + $this->assertEquals('1.3', $info->getVersion()); + } + + protected function setUp() + { + $this->describer = new SwaggerPhpDescriber(__DIR__.'/../Fixtures'); + } +} diff --git a/Tests/Functional/Fixtures/SwaggerPhp/Info.php b/Tests/Fixtures/SwaggerPhp/Info.php similarity index 100% rename from Tests/Functional/Fixtures/SwaggerPhp/Info.php rename to Tests/Fixtures/SwaggerPhp/Info.php diff --git a/Tests/Functional/Controller/ApiController.php b/Tests/Functional/Controller/ApiController.php index 382149f..8fd84d8 100644 --- a/Tests/Functional/Controller/ApiController.php +++ b/Tests/Functional/Controller/ApiController.php @@ -24,7 +24,7 @@ class ApiController } /** - * @Route("/nelmio", methods={"POST"}) + * @Route("/nelmio/{foo}", methods={"POST"}) * @ApiDoc( * description="This action is described." * ) diff --git a/Tests/Functional/FunctionalTest.php b/Tests/Functional/FunctionalTest.php index 7da4091..203707f 100644 --- a/Tests/Functional/FunctionalTest.php +++ b/Tests/Functional/FunctionalTest.php @@ -22,7 +22,7 @@ class FunctionalTest extends WebTestCase $this->assertEquals(['https'], $operation->getSchemes()); $this->assertEmpty($operation->getSummary()); $this->assertEmpty($operation->getDescription()); - $this->assertFalse($operation->getDeprecated()); + $this->assertNull($operation->getDeprecated()); $parameters = $operation->getParameters(); $this->assertTrue($parameters->has('user', 'path')); @@ -35,10 +35,14 @@ class FunctionalTest extends WebTestCase public function testNelmioAction() { - $operation = $this->getOperation('/nelmio', 'post'); + $operation = $this->getOperation('/nelmio/{foo}', 'post'); $this->assertEquals('This action is described.', $operation->getDescription()); $this->assertFalse($operation->getDeprecated()); + + $foo = $operation->getParameters()->get('foo', 'path'); + $this->assertTrue($foo->getRequired()); + $this->assertEquals('string', $foo->getType()); } public function testDeprecatedAction() @@ -50,13 +54,12 @@ class FunctionalTest extends WebTestCase $this->assertTrue($operation->getDeprecated()); } - public function testSwaggerPhpInfo() + public function testApiPlatform() { - $api = $this->getSwaggerDefinition(); - $info = $api->getInfo(); - - $this->assertEquals('My Awesome App', $info->getTitle()); - $this->assertEquals('1.3', $info->getVersion()); + $operation = $this->getOperation('/api/dummies', 'get'); + $operation = $this->getOperation('/api/foo', 'get'); + $operation = $this->getOperation('/api/foo', 'post'); + $operation = $this->getOperation('/api/dummies/{id}', 'get'); } private function getSwaggerDefinition() diff --git a/composer.json b/composer.json index f8664c1..d4a997d 100644 --- a/composer.json +++ b/composer.json @@ -11,7 +11,7 @@ "require": { "php": "^7.0", "symfony/framework-bundle": "^3.2", - "exsyst/swagger": "~0.1" + "exsyst/swagger": "dev-master" }, "require-dev": { "symfony/validator": "^3.2", @@ -24,7 +24,7 @@ "phpdocumentor/reflection-docblock": "^3.1", "phpunit/phpunit": "^5.4", "zircote/swagger-php": "^2.0", - "api-platform/core": "^2.0" + "api-platform/core": "dev-master" }, "suggest": { "nelmio/api-doc-bundle": "For using the ApiDoc annotation.",