diff --git a/Examples/polymorphism/polymorphism-3.1.0.yaml b/Examples/polymorphism/polymorphism-3.1.0.yaml index 7e43d558b..07413a1aa 100644 --- a/Examples/polymorphism/polymorphism-3.1.0.yaml +++ b/Examples/polymorphism/polymorphism-3.1.0.yaml @@ -45,6 +45,7 @@ components: - $ref: '#/components/schemas/EmployeeResponsible' EmployeeResponsible: + type: object allOf: - $ref: '#/components/schemas/Responsible' @@ -56,6 +57,7 @@ components: type: string const: Virtual FlResponsible: + type: object allOf: - $ref: '#/components/schemas/Responsible' diff --git a/Examples/polymorphism/polymorphism.yaml b/Examples/polymorphism/polymorphism.yaml index 60b2d349d..606f41383 100644 --- a/Examples/polymorphism/polymorphism.yaml +++ b/Examples/polymorphism/polymorphism.yaml @@ -46,6 +46,7 @@ components: - $ref: '#/components/schemas/EmployeeResponsible' EmployeeResponsible: + type: object allOf: - $ref: '#/components/schemas/Responsible' @@ -59,6 +60,7 @@ components: enum: - Virtual FlResponsible: + type: object allOf: - $ref: '#/components/schemas/Responsible' diff --git a/Examples/using-interfaces/using-interfaces.yaml b/Examples/using-interfaces/using-interfaces.yaml index ad616de42..2ea4715f3 100644 --- a/Examples/using-interfaces/using-interfaces.yaml +++ b/Examples/using-interfaces/using-interfaces.yaml @@ -67,6 +67,7 @@ components: example: blue Product: title: 'Product model' + type: object allOf: - $ref: '#/components/schemas/ProductInterface' diff --git a/Examples/using-traits/using-traits.yaml b/Examples/using-traits/using-traits.yaml index cb7fa2c20..b13f664d7 100644 --- a/Examples/using-traits/using-traits.yaml +++ b/Examples/using-traits/using-traits.yaml @@ -46,6 +46,7 @@ components: schemas: BellsAndWhistles: title: 'Bells and Whistles trait' + type: object allOf: - $ref: '#/components/schemas/Bells' @@ -86,6 +87,7 @@ components: type: object Product: title: 'Product model' + type: object allOf: - $ref: '#/components/schemas/Colour' @@ -103,6 +105,7 @@ components: example: gong SimpleProduct: title: 'SimpleProduct model' + type: object allOf: - $ref: '#/components/schemas/Bells' @@ -115,6 +118,7 @@ components: example: 1 TrickyProduct: title: 'TrickyProduct model' + type: object allOf: - $ref: '#/components/schemas/SimpleProduct' diff --git a/src/Processors/AugmentSchemas.php b/src/Processors/AugmentSchemas.php index c7e03db09..d126bc280 100644 --- a/src/Processors/AugmentSchemas.php +++ b/src/Processors/AugmentSchemas.php @@ -57,28 +57,6 @@ public function __invoke(Analysis $analysis) continue; } - if (!Generator::isDefault($annotation->allOf)) { - $schema = null; - foreach ($annotation->allOf as $nestedSchema) { - if (!Generator::isDefault($nestedSchema->ref)) { - continue; - } - - $schema = $nestedSchema; - } - - if ($schema === null) { - $schema = new OA\Schema([ - '_context' => new Context(['generated' => true], $annotation->_context), - ]); - $analysis->addAnnotation($schema, $schema->_context); - $annotation->allOf[] = $schema; - } - - $schema->merge([$property], true); - break; - } - $annotation->merge([$property], true); break; } diff --git a/src/Processors/Concerns/MergePropertiesTrait.php b/src/Processors/Concerns/MergePropertiesTrait.php index aa46132bf..86748d8d1 100644 --- a/src/Processors/Concerns/MergePropertiesTrait.php +++ b/src/Processors/Concerns/MergePropertiesTrait.php @@ -35,21 +35,11 @@ protected function inheritFrom(Analysis $analysis, OA\Schema $schema, OA\Schema $analysis->addAnnotation($refSchema, $refSchema->_context); } - protected function mergeAnnotations(OA\Schema $schema, array $annotations, array &$existing): void - { - foreach ($annotations as $annotation) { - if ($annotation instanceof OA\Property && !in_array($annotation->_context->property, $existing, true)) { - $existing[] = $annotation->_context->property; - $schema->merge([$annotation], true); - } - } - } - protected function mergeProperties(OA\Schema $schema, array $from, array &$existing): void { - foreach ($from['properties'] as $method) { - if (is_iterable($method->annotations)) { - foreach ($method->annotations as $annotation) { + foreach ($from['properties'] as $context) { + if (is_iterable($context->annotations)) { + foreach ($context->annotations as $annotation) { if ($annotation instanceof OA\Property && !in_array($annotation->_context->property, $existing, true)) { $existing[] = $annotation->_context->property; $schema->merge([$annotation], true); @@ -61,9 +51,9 @@ protected function mergeProperties(OA\Schema $schema, array $from, array &$exist protected function mergeMethods(OA\Schema $schema, array $from, array &$existing): void { - foreach ($from['methods'] as $method) { - if (is_iterable($method->annotations)) { - foreach ($method->annotations as $annotation) { + foreach ($from['methods'] as $context) { + if (is_iterable($context->annotations)) { + foreach ($context->annotations as $annotation) { if ($annotation instanceof OA\Property && !in_array($annotation->_context->property, $existing, true)) { $existing[] = $annotation->_context->property; $schema->merge([$annotation], true); diff --git a/src/Processors/ExpandClasses.php b/src/Processors/ExpandClasses.php index a462241f1..b7bea864c 100644 --- a/src/Processors/ExpandClasses.php +++ b/src/Processors/ExpandClasses.php @@ -9,7 +9,6 @@ use OpenApi\Analysis; use OpenApi\Annotations as OA; use OpenApi\Attributes as OAT; -use OpenApi\Context; use OpenApi\Generator; /** @@ -17,8 +16,7 @@ * - if the ancestor has a schema * => inherit from the ancestor if it has a schema (allOf) and stop. * - else - * => merge ancestor properties into the schema - * => merge ancestor trait properties into the schema (recursively). + * => merge ancestor properties into the schema. */ class ExpandClasses { @@ -44,14 +42,6 @@ public function __invoke(Analysis $analysis) } else { $this->mergeMethods($schema, $ancestor, $existing); $this->mergeProperties($schema, $ancestor, $existing); - - $traits = $analysis->getTraitsOfClass($schema->_context->fullyQualifiedName($ancestor['class']), true); - foreach ($traits as $definition) { - /** @var Context $context */ - foreach ($definition['properties'] as $context) { - $this->mergeAnnotations($schema, $context->annotations, $existing); - } - } } } } diff --git a/src/Processors/ExpandTraits.php b/src/Processors/ExpandTraits.php index 3a52d65f6..bdcdabcdf 100644 --- a/src/Processors/ExpandTraits.php +++ b/src/Processors/ExpandTraits.php @@ -25,10 +25,10 @@ public function __invoke(Analysis $analysis) /** @var OA\Schema[] $schemas */ $schemas = $analysis->getAnnotationsOfType([OA\Schema::class, OAT\Schema::class], true); + // do regular trait inheritance / merge foreach ($schemas as $schema) { - if ($schema->_context->is('class') || $schema->_context->is('trait')) { - $source = $schema->_context->class ?: $schema->_context->trait; - $traits = $analysis->getTraitsOfClass($schema->_context->fullyQualifiedName($source), true); + if ($schema->_context->is('trait')) { + $traits = $analysis->getTraitsOfClass($schema->_context->fullyQualifiedName($schema->_context->trait), true); $existing = []; foreach ($traits as $trait) { $traitSchema = $analysis->getSchemaForSource($trait['context']->fullyQualifiedName($trait['trait'])); @@ -36,7 +36,40 @@ public function __invoke(Analysis $analysis) $refPath = !Generator::isDefault($traitSchema->schema) ? $traitSchema->schema : $trait['trait']; $this->inheritFrom($analysis, $schema, $traitSchema, $refPath, $trait['context']); } else { - if ($schema->_context->is('class')) { + $this->mergeMethods($schema, $trait, $existing); + $this->mergeProperties($schema, $trait, $existing); + } + } + } + } + + foreach ($schemas as $schema) { + if ($schema->_context->is('class') && !$schema->_aux) { + // look at class traits + $traits = $analysis->getTraitsOfClass($schema->_context->fullyQualifiedName($schema->_context->class), true); + $existing = []; + foreach ($traits as $trait) { + $traitSchema = $analysis->getSchemaForSource($trait['context']->fullyQualifiedName($trait['trait'])); + if ($traitSchema) { + $refPath = !Generator::isDefault($traitSchema->schema) ? $traitSchema->schema : $trait['trait']; + $this->inheritFrom($analysis, $schema, $traitSchema, $refPath, $trait['context']); + } else { + $this->mergeMethods($schema, $trait, $existing); + $this->mergeProperties($schema, $trait, $existing); + } + } + + // also merge ancestor traits of non schema parents + $ancestors = $analysis->getSuperClasses($schema->_context->fullyQualifiedName($schema->_context->class)); + $existing = []; + foreach ($ancestors as $ancestor) { + $ancestorSchema = $analysis->getSchemaForSource($ancestor['context']->fullyQualifiedName($ancestor['class'])); + if ($ancestorSchema) { + // stop here as we inherit everything above + break; + } else { + $traits = $analysis->getTraitsOfClass($schema->_context->fullyQualifiedName($ancestor['class']), true); + foreach ($traits as $trait) { $this->mergeMethods($schema, $trait, $existing); $this->mergeProperties($schema, $trait, $existing); } diff --git a/tests/Fixtures/Apis/basic.yaml b/tests/Fixtures/Apis/basic.yaml index 190c71110..8aec68530 100644 --- a/tests/Fixtures/Apis/basic.yaml +++ b/tests/Fixtures/Apis/basic.yaml @@ -129,6 +129,7 @@ components: Product: title: Product description: 'A Product.' + type: object allOf: - $ref: '#/components/schemas/NameTrait' @@ -144,12 +145,12 @@ components: example: null colour: $ref: '#/components/schemas/Colour' + releasedAt: + type: string id: description: 'The id.' format: int64 example: 1 - releasedAt: - type: string kind: description: 'The kind.' type: string diff --git a/tests/Fixtures/Scratch/MergeTraits.php b/tests/Fixtures/Scratch/MergeTraits.php index be5dfc9aa..57a8b704c 100644 --- a/tests/Fixtures/Scratch/MergeTraits.php +++ b/tests/Fixtures/Scratch/MergeTraits.php @@ -1,25 +1,13 @@ -assertCount(2, $extendedSchema->allOf); $this->assertEquals(Components::ref('Base'), $extendedSchema->allOf[0]->ref); - $this->assertEquals('nested', $extendedSchema->allOf[1]->properties[1]->property); - $this->assertEquals('extendedProperty', $extendedSchema->allOf[1]->properties[0]->property); + $this->assertEquals('nested', $extendedSchema->allOf[1]->properties[0]->property); + $this->assertEquals('extendedProperty', $extendedSchema->allOf[1]->properties[1]->property); $nestedSchema = $schemas[1]; $this->assertCount(2, $nestedSchema->allOf); diff --git a/tests/ScratchTest.php b/tests/ScratchTest.php index 47c7e828d..26b6fda71 100644 --- a/tests/ScratchTest.php +++ b/tests/ScratchTest.php @@ -40,7 +40,7 @@ public function testScratch(string $version, string $scratch, string $spec, arra $openapi = (new Generator($this->getTrackingLogger())) ->setVersion($version) ->generate([$scratch]); - //file_put_contents($spec, $openapi->toYaml()); + // file_put_contents($spec, $openapi->toYaml()); $this->assertSpecEquals($openapi, file_get_contents($spec)); } } diff --git a/tests/UtilTest.php b/tests/UtilTest.php index b11f725c1..152ab35de 100644 --- a/tests/UtilTest.php +++ b/tests/UtilTest.php @@ -27,6 +27,7 @@ public function testExclude(): void 'Parser', 'Analysers', 'Processors', + 'Scratch', 'TypedProperties.php', 'Unreferenced.php', 'UsingRefs.php',