Merge pull request #1261 from nelmio/model

Support @Model through `ref`
This commit is contained in:
Guilhem N 2018-03-22 19:05:23 +01:00 committed by GitHub
commit 61cda0161c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 141 additions and 71 deletions

View File

@ -6,6 +6,27 @@ CHANGELOG
* Add a documentation form extension. Use the ``documentation`` option to define how a form field is documented.
* Allow references to config definitions in controllers.
* Using `@Model` implicitely in `@SWG\Schema`, `@SWG\Items` and `@SWG\Property` is deprecated. Use `ref=@Model()` instead.
Before:
```php
/**
* This was considered as an array of models.
*
* @SWG\Property(@Model(type=FooClass::class))
*/
```
After:
```php
/**
* For an individual object:
* @SWG\Property(ref=@Model(type=FooClass::class))
*
* For an array:
* @SWG\Property(type="array", @SWG\Items(ref=@Model(type=FooClass::class)))
*/
```
Config
* `nelmio_api_doc.areas` added support to filter by host patterns.

View File

@ -1,56 +1,6 @@
Frequently Asked Questions (FAQ)
================================
* Q: I use ``@Model`` to document an operation and the bundle understands I want an array of models while I only want one.
A: You most likely nested ``@Model`` in a ``@Schema`` annotation. The ``@Model`` annotation acts like a ``@Schema`` annotation, so
when nested, the bundle considers that you're documenting an array of models.
For instance, the following example::
/**
* @SWG\Response(
      *   response="200",
      *   description="Success",
* @SWG\Schema(@Model(type=User::class))
* )
*/
public function getUserAction()
{
}
will produce:
.. code-block:: yaml
# ...
responses:
200:
schema:
                  items: { $ref: '#/definitions/User' }
while you probably expected:
.. code-block:: yaml
# ...
responses:
200:
              schema: { $ref: '#/definitions/User' }
To obtain the output you expected, remove the ``@Schema`` annotation::
/**
* @SWG\Response(
* response="200",
* description="Success",
      *   @Model(type=User::class)
* )
*/
public function getUserAction()
{
}
* Q: How do I fix 404 or 406 HTTP status on NelmioApiDocBundle assets files (css, js, images)?
A: Just execute this command to solve it:

View File

@ -151,7 +151,7 @@ To document your routes, you can use the SwaggerPHP annotations and the
* description="Returns the rewards of an user",
* @SWG\Schema(
* type="array",
* @Model(type=Reward::class, groups={"full"})
* @SWG\Items(ref=@Model(type=Reward::class, groups={"full"}))
* )
* )
* @SWG\Parameter(
@ -175,7 +175,7 @@ Use models
----------
As shown in the example above, the bundle provides the ``@Model`` annotation.
When you use it, the bundle will deduce your model properties.
Use it instead of a definition reference and the bundle will deduce your model properties.
.. note::
@ -201,6 +201,27 @@ This annotation has two options:
    * )
    */
.. tip::
When used at the root of ``@SWG\Response`` and ``@SWG\Parameter``, ``@Model`` is automatically nested
in a ``@SWG\Schema``.
To use ``@Model`` directly within a ``@SWG\Schema``, ``@SWG\Items`` or ``@SWG\Property``, you have to use the ``$ref`` field::
/**
* @SWG\Response(
* @SWG\Schema(ref=@Model(type=User::class))
* )
*
* or
*
* @SWG\Response(
* @SWG\Schema(type="object",
* @SWG\Property(property="foo", ref=@Model(FooClass::class))
* )
* )
*/
Symfony Form types
~~~~~~~~~~~~~~~~~~

View File

@ -15,7 +15,6 @@ use Nelmio\ApiDocBundle\Annotation\Model as ModelAnnotation;
use Nelmio\ApiDocBundle\Model\Model;
use Nelmio\ApiDocBundle\Model\ModelRegistry;
use Swagger\Analysis;
use Swagger\Annotations\Definition;
use Swagger\Annotations\Items;
use Swagger\Annotations\Parameter;
use Swagger\Annotations\Response;
@ -40,6 +39,25 @@ final class ModelRegister
{
$modelsRegistered = [];
foreach ($analysis->annotations as $annotation) {
// @Model using the ref field
if ($annotation instanceof Schema && $annotation->ref instanceof ModelAnnotation) {
$model = $annotation->ref;
$annotation->ref = $this->modelRegistry->register(new Model($this->createType($model->type), $model->groups));
foreach ($annotation->_unmerged as $key => $unmerged) {
if ($unmerged === $model) {
unset($annotation->_unmerged[$key]);
break;
}
}
$analysis->annotations->detach($model);
continue;
}
// Implicit usages
if ($annotation instanceof Response) {
$annotationClass = Schema::class;
} elseif ($annotation instanceof Parameter) {
@ -72,12 +90,12 @@ final class ModelRegister
continue;
}
$ref = $this->modelRegistry->register(new Model($this->createType($model->type), $model->groups));
$parts = explode('/', $ref);
$modelsRegistered[end($parts)] = true;
if ($annotation instanceof Schema) {
@trigger_error(sprintf('Using `@Model` implicitely in a `@SWG\Schema`, `@SWG\Items` or `@SWG\Property` annotation in %s is deprecated since version 3.2 and won\'t be supported in 4.0. Use `ref=@Model()` instead.', $annotation->_context->getDebugLocation()), E_USER_DEPRECATED);
}
$annotation->merge([new $annotationClass([
'ref' => $ref,
'ref' => $this->modelRegistry->register(new Model($this->createType($model->type), $model->groups)),
])]);
// It is no longer an unmerged annotation
@ -90,10 +108,6 @@ final class ModelRegister
}
$analysis->annotations->detach($model);
}
foreach ($modelsRegistered as $model => $v) {
$analysis->annotations->attach(new Definition(['definition' => $model]));
}
}
private function createType(string $type): Type

View File

@ -33,7 +33,7 @@ class ApiController
* @SWG\Response(
* response="200",
* description="Success",
* @Model(type=Article::class, groups={"light"})
* @SWG\Schema(ref=@Model(type=Article::class, groups={"light"}))
* )
* @Route("/article/{id}", methods={"GET"})
*/
@ -65,7 +65,7 @@ class ApiController
* description="This is a parameter",
* @SWG\Schema(
* type="array",
* @Model(type=User::class)
* @SWG\Items(ref=@Model(type=User::class))
* )
* )
* @SWG\Tag(name="implicit")
@ -85,10 +85,7 @@ class ApiController
* name="foo",
* in="body",
* description="This is a parameter",
* @SWG\Schema(
* type="array",
* @Model(type=UserType::class)
* )
* @SWG\Schema(ref=@Model(type=UserType::class))
* )
*/
public function submitUserTypeAction()
@ -152,7 +149,7 @@ class ApiController
* name="form",
* in="body",
* description="Request content",
* @Model(type=DummyType::class)
* @SWG\Schema(ref=@Model(type=DummyType::class))
* )
* @SWG\Response(response="201", description="")
*/
@ -175,7 +172,7 @@ class ApiController
* @SWG\Response(
* response="201",
* description="Used for symfony constraints test",
* @Model(type=SymfonyConstraints::class)
* @SWG\Schema(ref=@Model(type=SymfonyConstraints::class))
* )
*/
public function symfonyConstraintsAction()

View File

@ -0,0 +1,67 @@
<?php
/*
* This file is part of the NelmioApiDocBundle package.
*
* (c) Nelmio
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Nelmio\ApiDocBundle\Tests\Swagger;
use Doctrine\Common\Annotations\AnnotationReader;
use EXSyst\Component\Swagger\Schema;
use EXSyst\Component\Swagger\Swagger;
use Nelmio\ApiDocBundle\Annotation\Model as ModelAnnotation;
use Nelmio\ApiDocBundle\Model\Model;
use Nelmio\ApiDocBundle\Model\ModelRegistry;
use Nelmio\ApiDocBundle\ModelDescriber\ModelDescriberInterface;
use Nelmio\ApiDocBundle\SwaggerPhp\ModelRegister;
use PHPUnit\Framework\TestCase;
use Swagger\Analysis;
use Swagger\Annotations as SWG;
class ModelRegisterTest extends TestCase
{
/**
* @group legacy
* @expectedDeprecation Using `@Model` implicitely in a `@SWG\Schema`, `@SWG\Items` or `@SWG\Property` annotation in %s. Use `ref=@Model()` instead.
*/
public function testDeprecatedImplicitUseOfModel()
{
$api = new Swagger();
$registry = new ModelRegistry([new NullModelDescriber()], $api);
$modelRegister = new ModelRegister($registry);
$annotationsReader = new AnnotationReader();
$modelRegister->__invoke(new Analysis([$annotation = $annotationsReader->getPropertyAnnotation(
new \ReflectionProperty(Foo::class, 'bar'),
SWG\Property::class
)]));
$this->assertEquals(['items' => ['$ref' => '#/definitions/Foo']], json_decode(json_encode($annotation), true));
}
}
class Foo
{
/**
* @SWG\Property(@ModelAnnotation(type=Foo::class))
*/
private $bar;
}
class NullModelDescriber implements ModelDescriberInterface
{
public function describe(Model $model, Schema $schema)
{
}
public function supports(Model $model): bool
{
return true;
}
}

View File

@ -140,7 +140,7 @@ class SwaggerDocblockConvertCommand extends ContainerAwareCommand
if (200 === $code && isset($responseMap[$code]['class'])) {
$model = $responseMap[$code]['class'];
$annotation .= ",
* @Model(type=\"$model\")";
* @SWG\\Schema(ref=@Model(type=\"$model\"))";
}
$annotation .= '
* )';

View File

@ -56,7 +56,7 @@
},
"extra": {
"branch-alias": {
"dev-master": "3.1.x-dev"
"dev-master": "3.2.x-dev"
}
}
}