From 52e7fc4de1024e8a9a183e1730f7417e60da402d Mon Sep 17 00:00:00 2001 From: Guilhem Niot Date: Sat, 30 Apr 2022 20:06:37 +0200 Subject: [PATCH] Use the same root context everywhere --- ApiDocGenerator.php | 16 ++++---- Model/ModelRegistry.php | 2 +- .../Annotations/OpenApiAnnotationsReader.php | 8 +++- .../ApplyOpenApiDiscriminatorTrait.php | 11 +++--- ModelDescriber/FormModelDescriber.php | 2 +- ModelDescriber/JMSModelDescriber.php | 2 +- OpenApiPhp/ModelRegister.php | 5 ++- OpenApiPhp/Util.php | 2 +- PropertyDescriber/ObjectPropertyDescriber.php | 3 +- RouteDescriber/FosRestDescriber.php | 2 +- .../SwaggerPHPApiComplianceTest.php | 39 +++++++++++++++++++ 11 files changed, 69 insertions(+), 23 deletions(-) create mode 100644 Tests/Functional/SwaggerPHPApiComplianceTest.php diff --git a/ApiDocGenerator.php b/ApiDocGenerator.php index 4ebafec..575bf56 100644 --- a/ApiDocGenerator.php +++ b/ApiDocGenerator.php @@ -91,7 +91,14 @@ final class ApiDocGenerator return !$processor instanceof \OpenApi\Processors\OperationId; })); - $this->openApi = new OpenApi([]); + $context = Util::createContext( + // BC for for zircote/swagger-php < 4.2 + method_exists($generator, 'getVersion') + ? ['version' => $generator->getVersion()] + : [] + ); + + $this->openApi = new OpenApi(['_context' => $context]); $modelRegistry = new ModelRegistry($this->modelDescribers, $this->openApi, $this->alternativeNames); if (null !== $this->logger) { $modelRegistry->setLogger($this->logger); @@ -103,13 +110,6 @@ final class ApiDocGenerator $describer->describe($this->openApi); } - - $context = Util::createContext( - // BC for for zircote/swagger-php < 4.2 - method_exists($generator, 'getVersion') - ? ['version' => $generator->getVersion()] - : [] - ); $analysis = new Analysis([], $context); $analysis->addAnnotation($this->openApi, $context); diff --git a/Model/ModelRegistry.php b/Model/ModelRegistry.php index db346fb..1957672 100644 --- a/Model/ModelRegistry.php +++ b/Model/ModelRegistry.php @@ -68,7 +68,7 @@ final class ModelRegistry } // Reserve the name - Util::getSchema($this->api, $this->names[$hash]); + $s = Util::getSchema($this->api, $this->names[$hash]); return OA\Components::SCHEMA_REF.$this->names[$hash]; } diff --git a/ModelDescriber/Annotations/OpenApiAnnotationsReader.php b/ModelDescriber/Annotations/OpenApiAnnotationsReader.php index 3325fb6..12d0520 100644 --- a/ModelDescriber/Annotations/OpenApiAnnotationsReader.php +++ b/ModelDescriber/Annotations/OpenApiAnnotationsReader.php @@ -39,11 +39,15 @@ class OpenApiAnnotationsReader public function updateSchema(\ReflectionClass $reflectionClass, OA\Schema $schema): void { + $this->setContext(Util::createContext([], $schema->_context)); + /** @var OA\Schema|null $oaSchema */ if (!$oaSchema = $this->getAnnotation($reflectionClass, OA\Schema::class)) { return; } + $this->setContext(null); + // Read @Model annotations $this->modelRegister->__invoke(new Analysis([$oaSchema], Util::createContext())); @@ -69,12 +73,12 @@ class OpenApiAnnotationsReader // In order to have nicer errors $declaringClass = $reflection->getDeclaringClass(); - $this->setContext(new Context([ + $this->setContext(Util::createContext([ 'namespace' => $declaringClass->getNamespaceName(), 'class' => $declaringClass->getShortName(), 'property' => $reflection->name, 'filename' => $declaringClass->getFileName(), - ])); + ], $property->_context)); /** @var OA\Property|null $oaProperty */ if (!$oaProperty = $this->getAnnotation($reflection, OA\Property::class)) { diff --git a/ModelDescriber/ApplyOpenApiDiscriminatorTrait.php b/ModelDescriber/ApplyOpenApiDiscriminatorTrait.php index 3c9446d..43c2b4e 100644 --- a/ModelDescriber/ApplyOpenApiDiscriminatorTrait.php +++ b/ModelDescriber/ApplyOpenApiDiscriminatorTrait.php @@ -13,6 +13,7 @@ namespace Nelmio\ApiDocBundle\ModelDescriber; use Nelmio\ApiDocBundle\Model\Model; use Nelmio\ApiDocBundle\Model\ModelRegistry; +use Nelmio\ApiDocBundle\OpenApiPhp\Util; use OpenApi\Annotations as OA; use Symfony\Component\PropertyInfo\Type; @@ -43,18 +44,18 @@ trait ApplyOpenApiDiscriminatorTrait array $typeMap ): void { $schema->oneOf = []; - $schema->discriminator = new OA\Discriminator([]); - $schema->discriminator->propertyName = $discriminatorProperty; - $schema->discriminator->mapping = []; + $discriminator = Util::getChild($schema, OA\Discriminator::class); + $discriminator->propertyName = $discriminatorProperty; + $discriminator->mapping = []; foreach ($typeMap as $propertyValue => $className) { - $oneOfSchema = new OA\Schema([]); + $oneOfSchema = Util::createChild($schema, OA\Schema::class); $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] = $oneOfSchema->ref; + $discriminator->mapping[$propertyValue] = $oneOfSchema->ref; } } } diff --git a/ModelDescriber/FormModelDescriber.php b/ModelDescriber/FormModelDescriber.php index 093422b..f93fc45 100644 --- a/ModelDescriber/FormModelDescriber.php +++ b/ModelDescriber/FormModelDescriber.php @@ -129,7 +129,7 @@ final class FormModelDescriber implements ModelDescriberInterface, ModelRegistry $ref = $this->modelRegistry->register($model); // We need to use allOf for description and title to be displayed if ($config->hasOption('documentation') && !empty($config->getOption('documentation'))) { - $property->allOf = [new OA\Schema(['ref' => $ref])]; + $property->allOf = [Util::createChild($property, OA\Schema::class, ['ref' => $ref])]; } else { $property->ref = $ref; } diff --git a/ModelDescriber/JMSModelDescriber.php b/ModelDescriber/JMSModelDescriber.php index 60a57ac..b4305a1 100644 --- a/ModelDescriber/JMSModelDescriber.php +++ b/ModelDescriber/JMSModelDescriber.php @@ -271,7 +271,7 @@ class JMSModelDescriber implements ModelDescriberInterface, ModelRegistryAwareIn if (empty($customFields)) { // no custom fields $property->ref = $modelRef; } else { - $property->allOf = [new OA\Schema(['ref' => $modelRef])]; + $property->allOf = [Util::createChild($property, OA\Schema::class, ['ref' => $modelRef])]; } $this->contexts[$model->getHash()] = $context; diff --git a/OpenApiPhp/ModelRegister.php b/OpenApiPhp/ModelRegister.php index 16dd6d0..defd3d4 100644 --- a/OpenApiPhp/ModelRegister.php +++ b/OpenApiPhp/ModelRegister.php @@ -14,6 +14,7 @@ namespace Nelmio\ApiDocBundle\OpenApiPhp; use Nelmio\ApiDocBundle\Annotation\Model as ModelAnnotation; use Nelmio\ApiDocBundle\Model\Model; use Nelmio\ApiDocBundle\Model\ModelRegistry; +use Nelmio\ApiDocBundle\OpenApiPhp\Util; use OpenApi\Analysis; use OpenApi\Annotations as OA; use Symfony\Component\PropertyInfo\Type; @@ -154,11 +155,11 @@ final class ModelRegister ) { switch ($type) { case 'json': - $modelAnnotation = new OA\JsonContent($properties); + $modelAnnotation = Util::createChild($annotation, OA\JsonContent::class, $properties); break; case 'xml': - $modelAnnotation = new OA\XmlContent($properties); + $modelAnnotation = Util::createChild($annotation, OA\XmlContent, $properties); break; default: diff --git a/OpenApiPhp/Util.php b/OpenApiPhp/Util.php index ea495e9..46be54f 100644 --- a/OpenApiPhp/Util.php +++ b/OpenApiPhp/Util.php @@ -84,7 +84,7 @@ final class Util public static function getSchema(OA\OpenApi $api, $schema): OA\Schema { if (!$api->components instanceof OA\Components) { - $api->components = new OA\Components([]); + $api->components = self::createChild($api, OA\Components::class, []); } return self::getIndexedCollectionItem($api->components, OA\Schema::class, $schema); diff --git a/PropertyDescriber/ObjectPropertyDescriber.php b/PropertyDescriber/ObjectPropertyDescriber.php index 34361d8..152b984 100644 --- a/PropertyDescriber/ObjectPropertyDescriber.php +++ b/PropertyDescriber/ObjectPropertyDescriber.php @@ -14,6 +14,7 @@ namespace Nelmio\ApiDocBundle\PropertyDescriber; use Nelmio\ApiDocBundle\Describer\ModelRegistryAwareInterface; use Nelmio\ApiDocBundle\Describer\ModelRegistryAwareTrait; use Nelmio\ApiDocBundle\Model\Model; +use Nelmio\ApiDocBundle\OpenApiPhp\Util; use OpenApi\Annotations as OA; use Symfony\Component\PropertyInfo\Type; @@ -37,7 +38,7 @@ class ObjectPropertyDescriber implements PropertyDescriberInterface, ModelRegist if ($types[0]->isNullable()) { $property->nullable = true; - $property->allOf = [new OA\Schema(['ref' => $this->modelRegistry->register(new Model($type, $groups))])]; + $property->allOf = [Util::createChild($property, OA\Schema::class, ['ref' => $this->modelRegistry->register(new Model($type, $groups))])]; return; } diff --git a/RouteDescriber/FosRestDescriber.php b/RouteDescriber/FosRestDescriber.php index c63249a..14df1bc 100644 --- a/RouteDescriber/FosRestDescriber.php +++ b/RouteDescriber/FosRestDescriber.php @@ -145,7 +145,7 @@ final class FosRestDescriber implements RouteDescriberInterface throw new \InvalidArgumentException('Unsupported media type'); } if (!isset($requestBody->content[$contentType])) { - $requestBody->content[$contentType] = new OA\MediaType( + $requestBody->content[$contentType] = Util::createChild($requestBody, OA\MediaType::class, [ 'mediaType' => $contentType, ] diff --git a/Tests/Functional/SwaggerPHPApiComplianceTest.php b/Tests/Functional/SwaggerPHPApiComplianceTest.php new file mode 100644 index 0000000..bff75b2 --- /dev/null +++ b/Tests/Functional/SwaggerPHPApiComplianceTest.php @@ -0,0 +1,39 @@ + 'api.example.com']); + } + + public function testAllContextsHaveSameRoot() + { + $openApi = $this->getOpenApiDefinition(); + $root = $openApi->_context; + + $counter = 0; + foreach ((new Analysis([$openApi], Util::createContext()))->annotations as $annotation) { + $this->assertSame($annotation->_context->root(), $root); + } + } +}