Introduce a Trait to Build OpenAPI Discriminators

See https://swagger.io/docs/specification/data-models/inheritance-and-polymorphism/

This is the adapter layer that will be included in the various model
describers. The creation of the discriminator and the `oneOf` values is
a little finicky and I wanted it to be tested and centralized.
This commit is contained in:
Christopher Davis 2021-02-01 08:37:20 -06:00
parent 306aba97a4
commit d8626c2735
2 changed files with 148 additions and 0 deletions

View File

@ -0,0 +1,60 @@
<?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\ModelDescriber;
use Nelmio\ApiDocBundle\Model\Model;
use Nelmio\ApiDocBundle\Model\ModelRegistry;
use OpenApi\Annotations as OA;
use Symfony\Component\PropertyInfo\Type;
/**
* Contains helper methods that add `discriminator` and `oneOf` values to
* Open API schemas to support poly morphism.
*
* @see https://swagger.io/docs/specification/data-models/inheritance-and-polymorphism/
* @internal
*/
trait ApplyOpenApiDiscriminatorTrait
{
/**
* @param Model $model the model that's being described, This is used to pass groups and config
* down to the children models in `oneOf`
* @param OA\Schema $schema The Open API schema to which `oneOf` and `discriminator` properties
* will be added
* @param string $discriminatorProperty The property that determine which model will be unsierailized
* @param array<string, string> $typeMap the map of $discriminatorProperty values to their
* types
*/
protected function applyOpenApiDiscriminator(
Model $model,
OA\Schema $schema,
ModelRegistry $modelRegistry,
string $discriminatorProperty,
array $typeMap
) : void {
$schema->oneOf = [];
$schema->discriminator = new OA\Discriminator([]);
$schema->discriminator->propertyName = $discriminatorProperty;
$schema->discriminator->mapping = [];
foreach ($typeMap as $propertyValue => $className) {
$oneOfSchema = new OA\Schema([]);
$oneOfSchema->ref = $modelRegistry->register(new Model(
new Type(Type::BUILTIN_TYPE_OBJECT, false, $className),
$model->getGroups(),
$model->getOptions()
));
$schema->oneOf[] = $oneOfSchema;
$schema->discriminator->mapping[$propertyValue] = clone $oneOfSchema;
}
}
}

View File

@ -0,0 +1,88 @@
<?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\ModelDescriber;
use Doctrine\Common\Annotations\AnnotationReader;
use Nelmio\ApiDocBundle\Model\Model;
use Nelmio\ApiDocBundle\Model\ModelRegistry;
use Nelmio\ApiDocBundle\ModelDescriber\ApplyOpenApiDiscriminatorTrait;
use OpenApi\Annotations as OA;
use Symfony\Component\PropertyInfo\Type;
use PHPUnit\Framework\TestCase;
class ApplyOpenApiDiscriminatorTraitTest extends TestCase
{
use ApplyOpenApiDiscriminatorTrait;
const GROUPS = ['test'];
const OPTIONS = ['test' => 123];
private $schema;
private $model;
public function testApplyAddsDiscriminatorProperty()
{
$this->applyOpenApiDiscriminator($this->model, $this->schema, $this->modelRegistry, 'type', [
'one' => 'FirstType',
'two' => 'SecondType',
]);
$this->assertInstanceOf(OA\Discriminator::class, $this->schema->discriminator);
$this->assertSame('type', $this->schema->discriminator->propertyName);
$this->assertArrayHasKey('one', $this->schema->discriminator->mapping);
$this->assertSame(
$this->modelRegistry->register($this->createModel('FirstType')),
$this->schema->discriminator->mapping['one']->ref
);
$this->assertArrayHasKey('two', $this->schema->discriminator->mapping);
$this->assertSame(
$this->modelRegistry->register($this->createModel('SecondType')),
$this->schema->discriminator->mapping['two']->ref
);
}
public function testApplyAddsOneOfFieldToSchema()
{
$this->applyOpenApiDiscriminator($this->model, $this->schema, $this->modelRegistry, 'type', [
'one' => 'FirstType',
'two' => 'SecondType',
]);
$this->assertNotSame(OA\UNDEFINED, $this->schema->oneOf);
$this->assertCount(2, $this->schema->oneOf);
$this->assertSame(
$this->modelRegistry->register($this->createModel('FirstType')),
$this->schema->oneOf[0]->ref
);
$this->assertSame(
$this->modelRegistry->register($this->createModel('SecondType')),
$this->schema->oneOf[1]->ref
);
}
protected function setUp() : void
{
$this->schema = new OA\Schema([]);
$this->model = $this->createModel(__CLASS__);
$this->modelRegistry = new ModelRegistry([], new OA\OpenApi([]));
}
private function createModel(string $className) : Model
{
return new Model(
new Type(Type::BUILTIN_TYPE_OBJECT, false, $className),
self::GROUPS,
self::OPTIONS
);
}
}