mirror of
https://github.com/retailcrm/NelmioApiDocBundle.git
synced 2025-02-03 08:09:25 +03:00
Merge pull request #991 from nelmio/UPGRADE
[3.0] Add a migration guide
This commit is contained in:
commit
cdf29801c8
@ -10,6 +10,10 @@ Version](https://poser.pugx.org/nelmio/api-doc-bundle/v/stable)](https://packagi
|
|||||||
The **NelmioApiDocBundle** bundle allows you to generate a decent documentation
|
The **NelmioApiDocBundle** bundle allows you to generate a decent documentation
|
||||||
for your APIs.
|
for your APIs.
|
||||||
|
|
||||||
|
## Migrate from 2.x to 3.0
|
||||||
|
|
||||||
|
[To migrate from 2.x to 3.0, just follow our guide.](https://github.com/nelmio/NelmioApiDocBundle/blob/dev/UPGRADE-3.0.md)
|
||||||
|
|
||||||
## Installation
|
## Installation
|
||||||
|
|
||||||
First, open a command console, enter your project directory and execute the following command to download the latest stable version of this bundle:
|
First, open a command console, enter your project directory and execute the following command to download the latest stable version of this bundle:
|
||||||
@ -38,6 +42,7 @@ class AppKernel extends Kernel
|
|||||||
To access your documentation in your browser, register the following route:
|
To access your documentation in your browser, register the following route:
|
||||||
|
|
||||||
```yml
|
```yml
|
||||||
|
# app/config/routing.yml
|
||||||
NelmioApiDocBundle:
|
NelmioApiDocBundle:
|
||||||
resource: "@NelmioApiDocBundle/Resources/config/routing/swaggerui.xml"
|
resource: "@NelmioApiDocBundle/Resources/config/routing/swaggerui.xml"
|
||||||
prefix: /api/doc
|
prefix: /api/doc
|
||||||
|
288
UPGRADE-3.0.md
Normal file
288
UPGRADE-3.0.md
Normal file
@ -0,0 +1,288 @@
|
|||||||
|
Upgrading From 2.x To 3.0
|
||||||
|
=========================
|
||||||
|
|
||||||
|
In 3.0 we did major changes. The biggest is the removal of the `@ApiDoc`
|
||||||
|
annotation. To help you migrate to 3.0, we created this guide.
|
||||||
|
|
||||||
|
Thanks to a command created by @dbu, it should not take too long.
|
||||||
|
|
||||||
|
Step 1: Migrate to Swagger-PHP commands
|
||||||
|
---------------------------------------
|
||||||
|
|
||||||
|
First, copy this command in ``src/AppBundle/Command/SwaggerDocblockConvertCommand.php``:
|
||||||
|
|
||||||
|
```php
|
||||||
|
<?php
|
||||||
|
|
||||||
|
// src/AppBundle/Command/SwaggerDocblockConvertCommand.php
|
||||||
|
namespace AppBundle\Command;
|
||||||
|
|
||||||
|
use Nelmio\ApiDocBundle\Annotation\ApiDoc;
|
||||||
|
use Symfony\Bundle\FrameworkBundle\Command\ContainerAwareCommand;
|
||||||
|
use Symfony\Component\Console\Input\InputInterface;
|
||||||
|
use Symfony\Component\Console\Output\OutputInterface;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Converts ApiDoc annotations to Swagger-PHP annotations.
|
||||||
|
*
|
||||||
|
* @author David Buchmann <david@liip.ch>
|
||||||
|
* @author Guilhem Niot <guilhem.niot@gmail.com>
|
||||||
|
*/
|
||||||
|
class SwaggerDocblockConvertCommand extends ContainerAwareCommand
|
||||||
|
{
|
||||||
|
protected function configure()
|
||||||
|
{
|
||||||
|
$this
|
||||||
|
->setDescription('')
|
||||||
|
->setName('api:doc:convert')
|
||||||
|
;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function execute(InputInterface $input, OutputInterface $output)
|
||||||
|
{
|
||||||
|
$extractor = $this->getContainer()->get('nelmio_api_doc.extractor.api_doc_extractor');
|
||||||
|
$apiDocs = $extractor->extractAnnotations($extractor->getRoutes());
|
||||||
|
|
||||||
|
foreach ($apiDocs as $annotation) {
|
||||||
|
/** @var ApiDoc $apiDoc */
|
||||||
|
$apiDoc = $annotation['annotation'];
|
||||||
|
|
||||||
|
$refl = $extractor->getReflectionMethod($apiDoc->getRoute()->getDefault('_controller'));
|
||||||
|
|
||||||
|
$this->rewriteClass($refl->getFileName(), $refl, $apiDoc);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Rewrite class with correct apidoc.
|
||||||
|
*/
|
||||||
|
private function rewriteClass(string $path, \ReflectionMethod $method, ApiDoc $apiDoc)
|
||||||
|
{
|
||||||
|
echo "Processing $path::{$method->name}\n";
|
||||||
|
$code = file_get_contents($path);
|
||||||
|
$old = $this->locateNelmioAnnotation($code, $method->name);
|
||||||
|
|
||||||
|
$code = substr_replace($code, $this->renderSwaggerAnnotation($apiDoc, $method), $old['start'], $old['length']);
|
||||||
|
$code = str_replace('use Nelmio\ApiDocBundle\Annotation\ApiDoc;', "use Nelmio\ApiDocBundle\Annotation\Model;\nuse Swagger\Annotations as SWG;", $code);
|
||||||
|
|
||||||
|
file_put_contents($path, $code);
|
||||||
|
}
|
||||||
|
|
||||||
|
private function renderSwaggerAnnotation(ApiDoc $apiDoc, \ReflectionMethod $method): string
|
||||||
|
{
|
||||||
|
$info = $apiDoc->toArray();
|
||||||
|
if ($apiDoc->getResource()) {
|
||||||
|
throw new \RuntimeException('implement me');
|
||||||
|
}
|
||||||
|
$path = str_replace('.{_format}', '', $apiDoc->getRoute()->getPath());
|
||||||
|
|
||||||
|
$annotation = '@SWG\\'.ucfirst(strtolower($apiDoc->getMethod())).'(
|
||||||
|
* tags={"'.$apiDoc->getSection().'"},
|
||||||
|
* summary="'.$this->escapeQuotes($apiDoc->getDescription()).'"';
|
||||||
|
|
||||||
|
foreach ($apiDoc->getFilters() as $name => $parameter) {
|
||||||
|
$description = array_key_exists('description', $parameter)
|
||||||
|
? $this->escapeQuotes($parameter['description'])
|
||||||
|
: 'todo';
|
||||||
|
|
||||||
|
$annotation .= ',
|
||||||
|
* @SWG\Parameter(
|
||||||
|
* name="'.$name.'",
|
||||||
|
* in="query",
|
||||||
|
* description="'.$description.'",
|
||||||
|
* required='.(array_key_exists($name, $apiDoc->getRequirements()) ? 'true' : 'false').',
|
||||||
|
* type="'.$this->determineDataType($parameter).'"
|
||||||
|
* )';
|
||||||
|
}
|
||||||
|
|
||||||
|
// Put parameters for POST requests into formData, as Swagger cannot handle more than one body parameter
|
||||||
|
$in = 'POST' === $apiDoc->getMethod()
|
||||||
|
? 'formData'
|
||||||
|
: 'body';
|
||||||
|
|
||||||
|
foreach ($apiDoc->getParameters() as $name => $parameter) {
|
||||||
|
$description = array_key_exists('description', $parameter)
|
||||||
|
? $this->escapeQuotes($parameter['description'])
|
||||||
|
: 'todo';
|
||||||
|
|
||||||
|
$annotation .= ',
|
||||||
|
* @SWG\Parameter(
|
||||||
|
* name="'.$name.'",
|
||||||
|
* in="'.$in.'",
|
||||||
|
* description="'.$description.'",
|
||||||
|
* required='.(array_key_exists($name, $apiDoc->getRequirements()) ? 'true' : 'false').',
|
||||||
|
* type="'.$this->determineDataType($parameter).'"';
|
||||||
|
|
||||||
|
if ('POST' !== $apiDoc->getMethod()) {
|
||||||
|
$annotation .= ',
|
||||||
|
* schema=""';
|
||||||
|
}
|
||||||
|
|
||||||
|
$annotation .= '
|
||||||
|
* )';
|
||||||
|
}
|
||||||
|
|
||||||
|
if (array_key_exists('statusCodes', $info)) {
|
||||||
|
$responses = $info['statusCodes'];
|
||||||
|
foreach ($responses as $code => $description) {
|
||||||
|
$responses[$code] = reset($description);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
$responses = [200 => 'Returned when successful'];
|
||||||
|
}
|
||||||
|
|
||||||
|
$responseMap = $apiDoc->getResponseMap();
|
||||||
|
foreach ($responses as $code => $description) {
|
||||||
|
$annotation .= ",
|
||||||
|
* @SWG\\Response(
|
||||||
|
* response=\"$code\",
|
||||||
|
* description=\"{$this->escapeQuotes($description)}\"";
|
||||||
|
if (200 === $code && isset($responseMap[$code]['class'])) {
|
||||||
|
$model = $responseMap[$code]['class'];
|
||||||
|
$annotation .= ",
|
||||||
|
* @Model(type=\"$model\")";
|
||||||
|
}
|
||||||
|
$annotation .= '
|
||||||
|
* )';
|
||||||
|
}
|
||||||
|
|
||||||
|
$annotation .= '
|
||||||
|
* )
|
||||||
|
*';
|
||||||
|
|
||||||
|
return $annotation;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return array with `start` position and `length`
|
||||||
|
*/
|
||||||
|
private function locateNelmioAnnotation(string $code, string $methodName): array
|
||||||
|
{
|
||||||
|
$position = strpos($code, "tion $methodName(");
|
||||||
|
if (false === $position) {
|
||||||
|
throw new \RuntimeException("Method $methodName not found in controller.");
|
||||||
|
}
|
||||||
|
|
||||||
|
$docstart = strrpos(substr($code, 0, $position), '@ApiDoc');
|
||||||
|
if (false === $docstart) {
|
||||||
|
throw new \RuntimeException("Method $methodName has no @ApiDoc annotation around\n".substr($code, $position - 200, 150));
|
||||||
|
}
|
||||||
|
$docend = strpos($code, '* )', $docstart) + 3;
|
||||||
|
|
||||||
|
return [
|
||||||
|
'start' => $docstart,
|
||||||
|
'length' => $docend - $docstart,
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
private function escapeQuotes(string $str): string
|
||||||
|
{
|
||||||
|
$lines = [];
|
||||||
|
foreach (explode("\n", $str) as $line) {
|
||||||
|
$lines[] = trim($line, ' *');
|
||||||
|
}
|
||||||
|
|
||||||
|
return str_replace('"', '""', implode(' ', $lines));
|
||||||
|
}
|
||||||
|
|
||||||
|
private function determineDataType(array $parameter): string
|
||||||
|
{
|
||||||
|
$dataType = isset($parameter['dataType']) ? $parameter['dataType'] : 'string';
|
||||||
|
$transform = [
|
||||||
|
'float' => 'number',
|
||||||
|
'datetime' => 'string',
|
||||||
|
];
|
||||||
|
if (array_key_exists($dataType, $transform)) {
|
||||||
|
$dataType = $transform[$dataType];
|
||||||
|
}
|
||||||
|
|
||||||
|
return $dataType;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Then open a command console, enter your project directory and run:
|
||||||
|
|
||||||
|
```
|
||||||
|
bin/console api:doc:convert
|
||||||
|
```
|
||||||
|
|
||||||
|
Your annotations should all be converted.
|
||||||
|
|
||||||
|
Note that this tool is here to help you but not all features are supported so
|
||||||
|
make sure the generated annotations still fit your needs.
|
||||||
|
|
||||||
|
Step 2: Update your routing
|
||||||
|
---------------------------
|
||||||
|
|
||||||
|
With NelmioApiDocBundle 2.x, you had to load the
|
||||||
|
``@NelmioApiDocBundle/Resources/config/routing.yml`` file. In 3.0, it was renamed
|
||||||
|
to ``@NelmioApiDocBundle/Resources/config/routing/swaggerui.xml``, so you have
|
||||||
|
to update the ``NelmioApiDocBundle`` route to:
|
||||||
|
|
||||||
|
```yml
|
||||||
|
# app/config/routing.yml
|
||||||
|
NelmioApiDocBundle:
|
||||||
|
resource: "@NelmioApiDocBundle/Resources/config/routing/swaggerui.xml"
|
||||||
|
prefix: /api/doc
|
||||||
|
```
|
||||||
|
|
||||||
|
Step 3: Update your config
|
||||||
|
--------------------------
|
||||||
|
|
||||||
|
* ``nelmio_api_doc.name`` was replaced by ``nelmio_api_doc.documentation.info.title``.
|
||||||
|
|
||||||
|
Before:
|
||||||
|
|
||||||
|
```yml
|
||||||
|
nelmio_api_doc:
|
||||||
|
name: My Awesome App!
|
||||||
|
```
|
||||||
|
|
||||||
|
After:
|
||||||
|
|
||||||
|
```yml
|
||||||
|
nelmio_api_doc:
|
||||||
|
documentation:
|
||||||
|
info:
|
||||||
|
title: My Awesome App!
|
||||||
|
```
|
||||||
|
|
||||||
|
* ``nelmio_api_doc.swagger.api_version`` was replaced by ``nelmio_api_doc.documentation.info.version``.
|
||||||
|
|
||||||
|
* ``nelmio_api_doc.swagger.info.title`` was replaced by ``nelmio_api_doc.documentation.info.title``.
|
||||||
|
|
||||||
|
* ``nelmio_api_doc.swagger.info.description`` was replaced by ``nelmio_api_doc.documentation.info.description``.
|
||||||
|
|
||||||
|
* Other options were removed.
|
||||||
|
|
||||||
|
Step 4: Update the bundle
|
||||||
|
-------------------------
|
||||||
|
|
||||||
|
Change the constraint of ``nelmio/api-doc-bundle`` in your ``composer.json`` file
|
||||||
|
to ``dev-dev``:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"require": {
|
||||||
|
"nelmio/api-doc-bundle": "dev-dev"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Then update your dependencies:
|
||||||
|
|
||||||
|
```
|
||||||
|
composer update
|
||||||
|
```
|
||||||
|
|
||||||
|
Step 5: Review the changes
|
||||||
|
--------------------------
|
||||||
|
|
||||||
|
It's almost finished!
|
||||||
|
|
||||||
|
As most of the changes were automated you should check that they did not break
|
||||||
|
anything. Run your test suite, review, do whatever you think is useful before
|
||||||
|
pushing the changes.
|
||||||
|
|
||||||
|
Then, commit the changes, push them, and enjoy!
|
Loading…
x
Reference in New Issue
Block a user