From eaf15112b415b8628a3d76e17f25607b005cb653 Mon Sep 17 00:00:00 2001 From: DjordyKoert Date: Mon, 8 Jan 2024 15:56:31 +0100 Subject: [PATCH 1/7] feat: move nullable openapi 3.1.0 conversion to processor --- src/Annotations/AbstractAnnotation.php | 16 ------- src/Generator.php | 1 + src/Processors/OpenApi31Processor.php | 65 ++++++++++++++++++++++++++ tests/Fixtures/Scratch/NullRef31.php | 3 +- tests/Fixtures/Scratch/NullRef31.yaml | 5 +- 5 files changed, 71 insertions(+), 19 deletions(-) create mode 100644 src/Processors/OpenApi31Processor.php diff --git a/src/Annotations/AbstractAnnotation.php b/src/Annotations/AbstractAnnotation.php index f3e7154e..962e987c 100644 --- a/src/Annotations/AbstractAnnotation.php +++ b/src/Annotations/AbstractAnnotation.php @@ -388,22 +388,6 @@ public function jsonSerialize() } if ($this->_context->version === OpenApi::VERSION_3_1_0) { - if (isset($data->nullable)) { - if (true === $data->nullable) { - if (isset($data->oneOf)) { - $data->oneOf[] = ['type' => 'null']; - } elseif (isset($data->anyOf)) { - $data->anyOf[] = ['type' => 'null']; - } elseif (isset($data->allOf)) { - $data->allOf[] = ['type' => 'null']; - } else { - $data->type = (array) $data->type; - $data->type[] = 'null'; - } - } - unset($data->nullable); - } - if (isset($data->minimum) && isset($data->exclusiveMinimum)) { if (true === $data->exclusiveMinimum) { $data->exclusiveMinimum = $data->minimum; diff --git a/src/Generator.php b/src/Generator.php index 8d5e3a2f..1ffd22c7 100644 --- a/src/Generator.php +++ b/src/Generator.php @@ -269,6 +269,7 @@ public function getProcessors(): array new Processors\MergeXmlContent(), new Processors\OperationId(), new Processors\CleanUnmerged(), + new Processors\OpenApi31Processor(), ]; } diff --git a/src/Processors/OpenApi31Processor.php b/src/Processors/OpenApi31Processor.php new file mode 100644 index 00000000..5e4a90a0 --- /dev/null +++ b/src/Processors/OpenApi31Processor.php @@ -0,0 +1,65 @@ +openapi->openapi !== OA\OpenApi::VERSION_3_1_0) { + return; + } + + $annotations = $analysis->getAnnotationsOfType(OA\AbstractAnnotation::class); + + foreach ($annotations as $annotation) { + $this->convertNullable($annotation); + } + } + + private function convertNullable(OA\AbstractAnnotation $annotation): void + { + if (!property_exists($annotation, 'nullable')) { + return; + } + + $nullable = $annotation->nullable; + $annotation->nullable = Generator::UNDEFINED; // Unregister nullable property + + if (true !== $nullable) { + return; + } + + if (property_exists($annotation, 'ref') && !Generator::isDefault($annotation->ref)) { + if (!property_exists($annotation, 'oneOf')) { + return; + } + + $annotation->oneOf = [new OA\Schema(['ref' => $annotation->ref]), new OA\Schema(['type' => 'null'])]; + $annotation->ref = Generator::UNDEFINED; + + return; + } + + if (property_exists($annotation, 'oneOf') && is_array($annotation->oneOf)) { + $annotation->oneOf[] = new OA\Schema(['type' => 'null']); + } elseif (property_exists($annotation, 'anyOf') && is_array($annotation->anyOf)) { + $annotation->anyOf[] = new OA\Schema(['type' => 'null']); + } elseif (property_exists($annotation, 'allOf') && is_array($annotation->allOf)) { + $annotation->allOf[] = new OA\Schema(['type' => 'null']); + } elseif (property_exists($annotation, 'type') && !Generator::isDefault($annotation->type)) { + $annotation->type = (array) $annotation->type; + $annotation->type[] = 'null'; + } + } +} diff --git a/tests/Fixtures/Scratch/NullRef31.php b/tests/Fixtures/Scratch/NullRef31.php index 2e6558b7..5f658d37 100644 --- a/tests/Fixtures/Scratch/NullRef31.php +++ b/tests/Fixtures/Scratch/NullRef31.php @@ -45,7 +45,8 @@ public function refonly() description: 'Ref plus response', content: new OAT\JsonContent( ref: '#/components/schemas/repository', - description: 'The repository', + description: 'Will either respond with something or null', + example: 'Some example about the content', nullable: true ) ), diff --git a/tests/Fixtures/Scratch/NullRef31.yaml b/tests/Fixtures/Scratch/NullRef31.yaml index 4e62d4bb..916b44ae 100644 --- a/tests/Fixtures/Scratch/NullRef31.yaml +++ b/tests/Fixtures/Scratch/NullRef31.yaml @@ -23,11 +23,12 @@ paths: description: 'Ref plus response' content: application/json: + example: 'Some example about the content' schema: oneOf: - - { $ref: '#/components/schemas/repository', description: 'The repository' } + - { $ref: '#/components/schemas/repository' } - { type: 'null' } - description: 'The repository' + description: 'Will either respond with something or null' components: schemas: repository: { } From 6a0269891defebb816e882032e9be647f4f5d07a Mon Sep 17 00:00:00 2001 From: DjordyKoert Date: Mon, 8 Jan 2024 16:01:26 +0100 Subject: [PATCH 2/7] only get schema types --- src/Processors/OpenApi31Processor.php | 21 +++++++++------------ 1 file changed, 9 insertions(+), 12 deletions(-) diff --git a/src/Processors/OpenApi31Processor.php b/src/Processors/OpenApi31Processor.php index 5e4a90a0..7f7ce4c9 100644 --- a/src/Processors/OpenApi31Processor.php +++ b/src/Processors/OpenApi31Processor.php @@ -20,19 +20,16 @@ public function __invoke(Analysis $analysis) return; } - $annotations = $analysis->getAnnotationsOfType(OA\AbstractAnnotation::class); + /** @var OA\Schema[] $annotations */ + $annotations = $analysis->getAnnotationsOfType(OA\Schema::class); foreach ($annotations as $annotation) { - $this->convertNullable($annotation); + $this->processNullable($annotation); } } - private function convertNullable(OA\AbstractAnnotation $annotation): void + private function processNullable(OA\Schema $annotation): void { - if (!property_exists($annotation, 'nullable')) { - return; - } - $nullable = $annotation->nullable; $annotation->nullable = Generator::UNDEFINED; // Unregister nullable property @@ -40,7 +37,7 @@ private function convertNullable(OA\AbstractAnnotation $annotation): void return; } - if (property_exists($annotation, 'ref') && !Generator::isDefault($annotation->ref)) { + if (!Generator::isDefault($annotation->ref)) { if (!property_exists($annotation, 'oneOf')) { return; } @@ -51,13 +48,13 @@ private function convertNullable(OA\AbstractAnnotation $annotation): void return; } - if (property_exists($annotation, 'oneOf') && is_array($annotation->oneOf)) { + if (is_array($annotation->oneOf)) { $annotation->oneOf[] = new OA\Schema(['type' => 'null']); - } elseif (property_exists($annotation, 'anyOf') && is_array($annotation->anyOf)) { + } elseif (is_array($annotation->anyOf)) { $annotation->anyOf[] = new OA\Schema(['type' => 'null']); - } elseif (property_exists($annotation, 'allOf') && is_array($annotation->allOf)) { + } elseif (is_array($annotation->allOf)) { $annotation->allOf[] = new OA\Schema(['type' => 'null']); - } elseif (property_exists($annotation, 'type') && !Generator::isDefault($annotation->type)) { + } elseif (!Generator::isDefault($annotation->type)) { $annotation->type = (array) $annotation->type; $annotation->type[] = 'null'; } From ee6189e264fa2ae60ede084b92f772f0403754ed Mon Sep 17 00:00:00 2001 From: DjordyKoert Date: Mon, 8 Jan 2024 16:08:55 +0100 Subject: [PATCH 3/7] feat: move exclusiveMinimum openapi 3.1.0 conversion to processor --- src/Annotations/AbstractAnnotation.php | 9 --------- src/Processors/OpenApi31Processor.php | 15 +++++++++++++++ 2 files changed, 15 insertions(+), 9 deletions(-) diff --git a/src/Annotations/AbstractAnnotation.php b/src/Annotations/AbstractAnnotation.php index 962e987c..78e91eaa 100644 --- a/src/Annotations/AbstractAnnotation.php +++ b/src/Annotations/AbstractAnnotation.php @@ -388,15 +388,6 @@ public function jsonSerialize() } if ($this->_context->version === OpenApi::VERSION_3_1_0) { - if (isset($data->minimum) && isset($data->exclusiveMinimum)) { - if (true === $data->exclusiveMinimum) { - $data->exclusiveMinimum = $data->minimum; - unset($data->minimum); - } elseif (false === $data->exclusiveMinimum) { - unset($data->exclusiveMinimum); - } - } - if (isset($data->maximum) && isset($data->exclusiveMaximum)) { if (true === $data->exclusiveMaximum) { $data->exclusiveMaximum = $data->maximum; diff --git a/src/Processors/OpenApi31Processor.php b/src/Processors/OpenApi31Processor.php index 7f7ce4c9..d8b21416 100644 --- a/src/Processors/OpenApi31Processor.php +++ b/src/Processors/OpenApi31Processor.php @@ -25,6 +25,7 @@ public function __invoke(Analysis $analysis) foreach ($annotations as $annotation) { $this->processNullable($annotation); + $this->processExclusiveMinimum($annotation); } } @@ -59,4 +60,18 @@ private function processNullable(OA\Schema $annotation): void $annotation->type[] = 'null'; } } + + private function processExclusiveMinimum(OA\Schema $annotation): void + { + if (Generator::UNDEFINED === $annotation->minimum || Generator::UNDEFINED === $annotation->exclusiveMinimum) { + return; + } + + if (true === $annotation->exclusiveMinimum) { + $annotation->exclusiveMinimum = $annotation->minimum; + $annotation->minimum = Generator::UNDEFINED; + } elseif (false === $annotation->exclusiveMinimum) { + $annotation->exclusiveMinimum = Generator::UNDEFINED; + } + } } From 0ae8749414096170a2fdef9f5a8920ea03ca5fc2 Mon Sep 17 00:00:00 2001 From: DjordyKoert Date: Mon, 8 Jan 2024 16:10:17 +0100 Subject: [PATCH 4/7] feat: move exclusiveMinimum openapi 3.1.0 conversion to processor --- src/Annotations/AbstractAnnotation.php | 11 ----------- src/Processors/OpenApi31Processor.php | 15 +++++++++++++++ 2 files changed, 15 insertions(+), 11 deletions(-) diff --git a/src/Annotations/AbstractAnnotation.php b/src/Annotations/AbstractAnnotation.php index 78e91eaa..b0051943 100644 --- a/src/Annotations/AbstractAnnotation.php +++ b/src/Annotations/AbstractAnnotation.php @@ -387,17 +387,6 @@ public function jsonSerialize() $data = (object) $ref; } - if ($this->_context->version === OpenApi::VERSION_3_1_0) { - if (isset($data->maximum) && isset($data->exclusiveMaximum)) { - if (true === $data->exclusiveMaximum) { - $data->exclusiveMaximum = $data->maximum; - unset($data->maximum); - } elseif (false === $data->exclusiveMaximum) { - unset($data->exclusiveMaximum); - } - } - } - return $data; } diff --git a/src/Processors/OpenApi31Processor.php b/src/Processors/OpenApi31Processor.php index d8b21416..20773854 100644 --- a/src/Processors/OpenApi31Processor.php +++ b/src/Processors/OpenApi31Processor.php @@ -26,6 +26,7 @@ public function __invoke(Analysis $analysis) foreach ($annotations as $annotation) { $this->processNullable($annotation); $this->processExclusiveMinimum($annotation); + $this->processExclusiveMaximum($annotation); } } @@ -74,4 +75,18 @@ private function processExclusiveMinimum(OA\Schema $annotation): void $annotation->exclusiveMinimum = Generator::UNDEFINED; } } + + private function processExclusiveMaximum(OA\Schema $annotation): void + { + if (Generator::UNDEFINED === $annotation->maximum || Generator::UNDEFINED === $annotation->exclusiveMaximum) { + return; + } + + if (true === $annotation->exclusiveMaximum) { + $annotation->exclusiveMaximum = $annotation->maximum; + $annotation->maximum = Generator::UNDEFINED; + } elseif (false === $annotation->exclusiveMaximum) { + $annotation->exclusiveMaximum = Generator::UNDEFINED; + } + } } From ad2bb4faac99831aaf75cc82db5d79d4189c87a2 Mon Sep 17 00:00:00 2001 From: DjordyKoert Date: Mon, 8 Jan 2024 16:12:26 +0100 Subject: [PATCH 5/7] remove processor handled nullable conversion --- src/Annotations/AbstractAnnotation.php | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/src/Annotations/AbstractAnnotation.php b/src/Annotations/AbstractAnnotation.php index b0051943..37e4a7eb 100644 --- a/src/Annotations/AbstractAnnotation.php +++ b/src/Annotations/AbstractAnnotation.php @@ -367,11 +367,8 @@ public function jsonSerialize() } if (property_exists($this, 'nullable') && $this->nullable === true) { $ref = ['oneOf' => [$ref]]; - if ($this->_context->version == OpenApi::VERSION_3_1_0) { - $ref['oneOf'][] = ['type' => 'null']; - } else { - $ref['nullable'] = $data->nullable; - } + $ref['nullable'] = $data->nullable; + unset($data->nullable); // preserve other properties From f5d0469744b8bbadfd0852b453d840caa4f2042b Mon Sep 17 00:00:00 2001 From: DjordyKoert Date: Mon, 8 Jan 2024 16:18:38 +0100 Subject: [PATCH 6/7] use isDefault() for default check --- src/Processors/OpenApi31Processor.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Processors/OpenApi31Processor.php b/src/Processors/OpenApi31Processor.php index 20773854..7e3025a4 100644 --- a/src/Processors/OpenApi31Processor.php +++ b/src/Processors/OpenApi31Processor.php @@ -64,7 +64,7 @@ private function processNullable(OA\Schema $annotation): void private function processExclusiveMinimum(OA\Schema $annotation): void { - if (Generator::UNDEFINED === $annotation->minimum || Generator::UNDEFINED === $annotation->exclusiveMinimum) { + if (Generator::isDefault($annotation->minimum) || Generator::isDefault($annotation->exclusiveMinimum)) { return; } @@ -78,7 +78,7 @@ private function processExclusiveMinimum(OA\Schema $annotation): void private function processExclusiveMaximum(OA\Schema $annotation): void { - if (Generator::UNDEFINED === $annotation->maximum || Generator::UNDEFINED === $annotation->exclusiveMaximum) { + if (Generator::isDefault($annotation->maximum) || Generator::isDefault($annotation->exclusiveMaximum)) { return; } From 2eedbb3a9693762720a7aff34d6a2af8303bc593 Mon Sep 17 00:00:00 2001 From: DjordyKoert Date: Mon, 8 Jan 2024 16:20:25 +0100 Subject: [PATCH 7/7] change oneOf overwriting check --- src/Processors/OpenApi31Processor.php | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/src/Processors/OpenApi31Processor.php b/src/Processors/OpenApi31Processor.php index 7e3025a4..186a3b92 100644 --- a/src/Processors/OpenApi31Processor.php +++ b/src/Processors/OpenApi31Processor.php @@ -24,12 +24,22 @@ public function __invoke(Analysis $analysis) $annotations = $analysis->getAnnotationsOfType(OA\Schema::class); foreach ($annotations as $annotation) { + $this->processReference($annotation); $this->processNullable($annotation); $this->processExclusiveMinimum($annotation); $this->processExclusiveMaximum($annotation); } } + private function processReference(OA\Schema $annotation): void + { + if (Generator::isDefault($annotation->ref)) { + return; + } + + + } + private function processNullable(OA\Schema $annotation): void { $nullable = $annotation->nullable; @@ -40,7 +50,7 @@ private function processNullable(OA\Schema $annotation): void } if (!Generator::isDefault($annotation->ref)) { - if (!property_exists($annotation, 'oneOf')) { + if (!Generator::isDefault($annotation->oneOf)) { return; }