From 2984dc10dd9ab55002a0c455cb754f2101119518 Mon Sep 17 00:00:00 2001 From: Sohel Ahmed Mesaniya Date: Wed, 11 Sep 2024 21:27:43 +0530 Subject: [PATCH 01/24] Tooling: dynamic container name to remove confusion created by multiple forks --- Makefile | 15 +++++++++++++++ docker-compose.override.dist.yml | 4 ++++ docker-compose.override.yml | 4 ++++ 3 files changed, 23 insertions(+) create mode 100644 docker-compose.override.dist.yml create mode 100644 docker-compose.override.yml diff --git a/Makefile b/Makefile index ee99b2ac..222d3e3f 100644 --- a/Makefile +++ b/Makefile @@ -15,6 +15,9 @@ DOCKER_PHP=docker-compose run --rm php DOCKER_NODE=docker-compose run --rm -w /app node endif +CONTAINER_NAME=$$(echo $$(pwd) | tr / _) + + all: @echo "the following commands are available:" @echo "" @@ -32,6 +35,12 @@ all: check-style: php-cs-fixer.phar PHP_CS_FIXER_IGNORE_ENV=1 ./php-cs-fixer.phar fix src/ --diff --dry-run +cli: + docker-compose exec --user=$(shell id -u) php bash + +cli_root: + docker-compose exec --user="root" php bash + fix-style: php-cs-fixer.phar $(DOCKER_PHP) vendor/bin/indent --tabs composer.json $(DOCKER_PHP) vendor/bin/indent --spaces .php_cs.dist @@ -81,5 +90,11 @@ coverage: .php-openapi-covA .php-openapi-covB .php-openapi-covB: grep -rhPo '^class \w+' src/spec/ | awk '{print $$2}' |grep -v '^Type$$' | sort > $@ +docker-compose.override.yml: docker-compose.override.dist.yml + test -f $@ || cp $< $@ + +start-docker: docker-compose.override.yml + CONTAINER_NAME=$(CONTAINER_NAME) docker-compose up -d + .PHONY: all check-style fix-style install test lint coverage diff --git a/docker-compose.override.dist.yml b/docker-compose.override.dist.yml new file mode 100644 index 00000000..30a8a6b9 --- /dev/null +++ b/docker-compose.override.dist.yml @@ -0,0 +1,4 @@ +version: "3" +services: + php: + container_name: "C_${CONTAINER_NAME}" diff --git a/docker-compose.override.yml b/docker-compose.override.yml new file mode 100644 index 00000000..30a8a6b9 --- /dev/null +++ b/docker-compose.override.yml @@ -0,0 +1,4 @@ +version: "3" +services: + php: + container_name: "C_${CONTAINER_NAME}" From b111f490d4557197fb4d79a0f014c15d2e42941c Mon Sep 17 00:00:00 2001 From: Sohel Ahmed Mesaniya Date: Fri, 13 Sep 2024 20:28:56 +0530 Subject: [PATCH 02/24] Tooling: dynamic container name to remove confusion created by multiple forks - 2 --- Makefile | 20 +++++++++++--------- docker-compose.override.dist.yml | 4 ---- docker-compose.override.yml | 4 ---- 3 files changed, 11 insertions(+), 17 deletions(-) delete mode 100644 docker-compose.override.dist.yml delete mode 100644 docker-compose.override.yml diff --git a/Makefile b/Makefile index 222d3e3f..b55fe4e3 100644 --- a/Makefile +++ b/Makefile @@ -15,8 +15,8 @@ DOCKER_PHP=docker-compose run --rm php DOCKER_NODE=docker-compose run --rm -w /app node endif -CONTAINER_NAME=$$(echo $$(pwd) | tr / _) - +CONTAINER_NAME=$$(echo $$(pwd) | tr / _ | cut -c 2-) +UID=$(shell id -u) all: @echo "the following commands are available:" @@ -36,10 +36,10 @@ check-style: php-cs-fixer.phar PHP_CS_FIXER_IGNORE_ENV=1 ./php-cs-fixer.phar fix src/ --diff --dry-run cli: - docker-compose exec --user=$(shell id -u) php bash + COMPOSE_PROJECT_NAME=$(CONTAINER_NAME) docker-compose exec --user=$(UID) php bash cli_root: - docker-compose exec --user="root" php bash + COMPOSE_PROJECT_NAME=$(CONTAINER_NAME) docker-compose exec --user="root" php bash fix-style: php-cs-fixer.phar $(DOCKER_PHP) vendor/bin/indent --tabs composer.json @@ -90,11 +90,13 @@ coverage: .php-openapi-covA .php-openapi-covB .php-openapi-covB: grep -rhPo '^class \w+' src/spec/ | awk '{print $$2}' |grep -v '^Type$$' | sort > $@ -docker-compose.override.yml: docker-compose.override.dist.yml - test -f $@ || cp $< $@ +build-docker: + COMPOSE_PROJECT_NAME=$(CONTAINER_NAME) docker-compose build -start-docker: docker-compose.override.yml - CONTAINER_NAME=$(CONTAINER_NAME) docker-compose up -d +start-docker: + COMPOSE_PROJECT_NAME=$(CONTAINER_NAME) docker-compose up -d -.PHONY: all check-style fix-style install test lint coverage +stop-docker: + COMPOSE_PROJECT_NAME=$(CONTAINER_NAME) docker-compose down --remove-orphans +.PHONY: all check-style fix-style install test lint coverage build-docker start-docker stop-docker diff --git a/docker-compose.override.dist.yml b/docker-compose.override.dist.yml deleted file mode 100644 index 30a8a6b9..00000000 --- a/docker-compose.override.dist.yml +++ /dev/null @@ -1,4 +0,0 @@ -version: "3" -services: - php: - container_name: "C_${CONTAINER_NAME}" diff --git a/docker-compose.override.yml b/docker-compose.override.yml deleted file mode 100644 index 30a8a6b9..00000000 --- a/docker-compose.override.yml +++ /dev/null @@ -1,4 +0,0 @@ -version: "3" -services: - php: - container_name: "C_${CONTAINER_NAME}" From 27e2618787ac21275cb2137e06bd35f202b51b6b Mon Sep 17 00:00:00 2001 From: Sohel Ahmed Mesaniya Date: Sat, 14 Sep 2024 20:30:45 +0530 Subject: [PATCH 03/24] Remove Composer dev dependencies apis-guru/openapi-directory, nexmo/api-specification temporarily for work-around for https://github.com/cebe/php-openapi/issues/209 and https://github.com/cebe/php-openapi/issues/210 --- composer.json | 2 -- 1 file changed, 2 deletions(-) diff --git a/composer.json b/composer.json index 46cb26f2..70b9a563 100644 --- a/composer.json +++ b/composer.json @@ -28,8 +28,6 @@ "phpunit/phpunit": "^6.5 || ^7.5 || ^8.5 || ^9.4", "oai/openapi-specification": "3.0.3", "mermade/openapi3-examples": "1.0.0", - "apis-guru/openapi-directory": "1.0.0", - "nexmo/api-specification": "1.0.0", "phpstan/phpstan": "^0.12.0" }, "conflict": { From b4c19c7599154029dc2035d42282b05237124d79 Mon Sep 17 00:00:00 2001 From: Sohel Ahmed Mesaniya Date: Sat, 14 Sep 2024 20:31:23 +0530 Subject: [PATCH 04/24] Temporary remove old PHPUnit config --- phpunit.xml.dist | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/phpunit.xml.dist b/phpunit.xml.dist index 484250ce..293ebd34 100644 --- a/phpunit.xml.dist +++ b/phpunit.xml.dist @@ -13,11 +13,7 @@ ./tests - - - ./src - - + ./src From bffeea72b5addf7f8b7631a7127ed9f837e1a61d Mon Sep 17 00:00:00 2001 From: Sohel Ahmed Mesaniya Date: Sat, 14 Sep 2024 20:33:32 +0530 Subject: [PATCH 05/24] Add failing test --- .gitignore | 1 + TODO.taskpaper | 10 +++++++ tests/spec/SchemaTest.php | 59 +++++++++++++++++++++++++++++++++++++++ 3 files changed, 70 insertions(+) create mode 100644 TODO.taskpaper diff --git a/.gitignore b/.gitignore index c86af6fe..00d6c0f2 100644 --- a/.gitignore +++ b/.gitignore @@ -8,3 +8,4 @@ /.phpunit.result.cache php-cs-fixer.phar +xdebug_remote.log diff --git a/TODO.taskpaper b/TODO.taskpaper new file mode 100644 index 00000000..48920c86 --- /dev/null +++ b/TODO.taskpaper @@ -0,0 +1,10 @@ +TODO.taskpaper + + ☐ undo commit + ☐ undo commit + ☐ create failing test + ☐ implement the solution + ☐ fix failing tests if any + ☐ resolve TODOs if any + ☐ review Merge Request + ☐ delete this file and submit Merge Request diff --git a/tests/spec/SchemaTest.php b/tests/spec/SchemaTest.php index 1600b3b0..5c80de5c 100644 --- a/tests/spec/SchemaTest.php +++ b/tests/spec/SchemaTest.php @@ -420,4 +420,63 @@ public function testPropertyNameRef() $this->assertEquals('string', $person->properties['name']->type); $this->assertEquals('string', $person->properties['$ref']->type); } + + // https://github.com/cebe/yii2-openapi/issues/165 + public function test165ResolveAllOf() + { + $openApi = Reader::readFromYaml(<<<'YAML' + + +openapi: 3.0.3 + +info: + title: Add validation rules by attribute name or pattern \#30 + version: 1.0.0 + +components: + schemas: + User: + type: object + required: + - id + - name + properties: + id: + type: integer + name: + type: string + Post: + type: object + properties: + id: + type: integer + content: + type: string + user: + allOf: + - $ref: '#/components/schemas/User' + - x-faker: false + +paths: + '/': + get: + responses: + '200': + description: OK + + +YAML + ); + + $openApi->resolveReferences(new \cebe\openapi\ReferenceContext($openApi, 'file:///tmp/openapi.yaml')); + $result = $openApi->validate(); + $this->assertTrue($result); + $this->assertEquals([], $openApi->getErrors()); + + // $this->assertTrue($openApi->components->schemas['Post']->getSerializableData()); + + // $openApi->components->schemas['Post']->resolveReferences(); + $this->assertTrue($openApi->components->schemas['Post']->getSerializableData()); + // $this->assertTrue($openApi->components->schemas['Post']->properties['user']->properties); + } } From 99dd8bed208c74788eded1dea2e729f2ac8b919b Mon Sep 17 00:00:00 2001 From: Sohel Ahmed Mesaniya Date: Fri, 20 Sep 2024 19:27:52 +0530 Subject: [PATCH 06/24] Implement --- TODO.taskpaper | 4 +- src/Reader.php | 2 + src/SpecBaseObject.php | 72 ++++++++++++++++++++++++++++++ tests/spec/SchemaTest.php | 60 ++++++------------------- tests/spec/data/resolve_all_of.yml | 49 ++++++++++++++++++++ 5 files changed, 138 insertions(+), 49 deletions(-) create mode 100644 tests/spec/data/resolve_all_of.yml diff --git a/TODO.taskpaper b/TODO.taskpaper index 48920c86..24d31516 100644 --- a/TODO.taskpaper +++ b/TODO.taskpaper @@ -1,10 +1,10 @@ TODO.taskpaper - ☐ undo commit - ☐ undo commit ☐ create failing test ☐ implement the solution ☐ fix failing tests if any ☐ resolve TODOs if any + ☐ undo commit https://github.com/cebe/php-openapi/pull/208/commits/27e2618787ac21275cb2137e06bd35f202b51b6b + ☐ undo commit https://github.com/cebe/php-openapi/pull/208/commits/b4c19c7599154029dc2035d42282b05237124d79 ☐ review Merge Request ☐ delete this file and submit Merge Request diff --git a/src/Reader.php b/src/Reader.php index 99ee5c36..5687699d 100644 --- a/src/Reader.php +++ b/src/Reader.php @@ -147,6 +147,8 @@ public static function readFromYamlFile(string $fileName, string $baseType = Ope } $spec->resolveReferences(); } + $spec->resolveAllOf(); + $spec->resolveAllOf2(); // TODO rename + refactor if possible return $spec; } } diff --git a/src/SpecBaseObject.php b/src/SpecBaseObject.php index 1de429bd..781ab44b 100644 --- a/src/SpecBaseObject.php +++ b/src/SpecBaseObject.php @@ -12,6 +12,7 @@ use cebe\openapi\json\JsonPointer; use cebe\openapi\json\JsonReference; use cebe\openapi\spec\Reference; +use cebe\openapi\spec\Schema; use cebe\openapi\spec\Type; /** @@ -525,4 +526,75 @@ public function getExtensions(): array } return $extensions; } + + public function getProperties(): array + { + return $this->_properties; + } + + public function mergeProperties($properties) + { + $this->_properties = array_merge($this->_properties, $properties); + } + + public function resolveAllOf($parent = null) + { + foreach ($this->_properties as $property => $value) { + if ($property === 'allOf' && !empty($value)) { + $this->_properties[$property] = $this->mergeAllAllOfsInToSingleObj($value); + } elseif ($value instanceof SpecObjectInterface && method_exists($value, 'resolveAllOf')) { + $value->resolveAllOf($this->_properties[$property]); + } elseif (is_array($value)) { + foreach ($value as $k => $item) { + if ($k === 'allOf' && !empty($item)) { + $this->_properties[$property][$k] = $this->mergeAllAllOfsInToSingleObj($item); + } elseif ($item instanceof SpecObjectInterface) { + $item->resolveAllOf($this->_properties[$property][$k]); + } + } + } + } + } + + public function mergeAllAllOfsInToSingleObj($allOfs): self + { + $allOfs = $this->allOf; + /** @var static $first */ + $first = $this->allOf[0]; + unset($allOfs[0]); + foreach ($allOfs as $allOf) { + /** @var Schema $allOf */ + $first->mergeProperties($allOf->getProperties()); + } + return $first; + } + + public function resolveAllOf2() // TODO + { + foreach ($this->_properties as $property => $value) { + if ($property === 'properties' && !empty($value)) { + foreach ($value as $k => $v) { + if (!empty($v->allOf)) { + $temp = $v->allOf; + $this->_properties[$property][$k] = $temp; + } + } + } elseif ($value instanceof SpecObjectInterface && method_exists($value, 'resolveAllOf2')) { + $value->resolveAllOf2(); + } elseif (is_array($value)) { + foreach ($value as $k2 => $item) { + if ($k2 === 'properties' && !empty($item)) { + foreach ($item as $kIn => $vIn) { // TODO rename var names + if (!empty($vIn->allOf)) { + $tempIn = $vIn->allOf; + $this->_properties[$property][$value][$k2][$kIn] = $tempIn; + } + } + } elseif ($item instanceof SpecObjectInterface) { + $item->resolveAllOf2(); + } + } + } + } + } } diff --git a/tests/spec/SchemaTest.php b/tests/spec/SchemaTest.php index 5c80de5c..815e6775 100644 --- a/tests/spec/SchemaTest.php +++ b/tests/spec/SchemaTest.php @@ -422,61 +422,27 @@ public function testPropertyNameRef() } // https://github.com/cebe/yii2-openapi/issues/165 + // TODO cleanup public function test165ResolveAllOf() { - $openApi = Reader::readFromYaml(<<<'YAML' - - -openapi: 3.0.3 - -info: - title: Add validation rules by attribute name or pattern \#30 - version: 1.0.0 - -components: - schemas: - User: - type: object - required: - - id - - name - properties: - id: - type: integer - name: - type: string - Post: - type: object - properties: - id: - type: integer - content: - type: string - user: - allOf: - - $ref: '#/components/schemas/User' - - x-faker: false - -paths: - '/': - get: - responses: - '200': - description: OK - - -YAML - ); - - $openApi->resolveReferences(new \cebe\openapi\ReferenceContext($openApi, 'file:///tmp/openapi.yaml')); +// $openApi->resolveReferences(new \cebe\openapi\ReferenceContext($openApi, 'file:///tmp/openapi.yaml')); + $openApi = Reader::readFromYamlFile(__DIR__ . '/data/resolve_all_of.yml'); $result = $openApi->validate(); +// $openApi->resolveAllOf(); $this->assertTrue($result); $this->assertEquals([], $openApi->getErrors()); // $this->assertTrue($openApi->components->schemas['Post']->getSerializableData()); +// $openApi->components->schemas['Post']->properties['user']->allOf = ['abc' => 'def']; +// $openApi->components->schemas['Post']->properties['user']->__set('allOf', ['abc' => 'def']); // $openApi->components->schemas['Post']->resolveReferences(); - $this->assertTrue($openApi->components->schemas['Post']->getSerializableData()); - // $this->assertTrue($openApi->components->schemas['Post']->properties['user']->properties); +// $this->assertTrue($openApi->components->schemas['Post']); +// $this->assertTrue($openApi->components->schemas['Post']->getSerializableData()); + $this->assertSame( + json_decode(json_encode($openApi->components->schemas['Post']->getSerializableData()), true) + + , [] + ); } } diff --git a/tests/spec/data/resolve_all_of.yml b/tests/spec/data/resolve_all_of.yml new file mode 100644 index 00000000..8c81afe7 --- /dev/null +++ b/tests/spec/data/resolve_all_of.yml @@ -0,0 +1,49 @@ + +openapi: 3.0.3 + +info: + title: Resolve allOf + version: 1.0.0 + +components: + schemas: + User: + type: object + required: + - id + - name + properties: + id: + type: integer + name: + type: string +# User2: +# type: object +# required: +# - id2 +# - name +# properties: +# id2: +# type: integer +# name: +# type: array + Post: + type: object + properties: + id: + type: integer + content: + type: string + user: + allOf: + - $ref: '#/components/schemas/User' +# - $ref: '#/components/schemas/User2' + - x-faker: false + - x-faker2: true + +paths: + '/': + get: + responses: + '200': + description: OK From 35a9f9062eed91d4e7132883d6206a9a3f0ad0ae Mon Sep 17 00:00:00 2001 From: Sohel Ahmed Mesaniya Date: Fri, 20 Sep 2024 20:41:36 +0530 Subject: [PATCH 07/24] Refactor + fix bug WIP --- src/SpecBaseObject.php | 20 ++++++++++---------- tests/spec/SchemaTest.php | 5 ++++- tests/spec/data/resolve_all_of.yml | 22 +++++++++++----------- 3 files changed, 25 insertions(+), 22 deletions(-) diff --git a/src/SpecBaseObject.php b/src/SpecBaseObject.php index 781ab44b..ed2476d0 100644 --- a/src/SpecBaseObject.php +++ b/src/SpecBaseObject.php @@ -534,29 +534,29 @@ public function getProperties(): array public function mergeProperties($properties) { - $this->_properties = array_merge($this->_properties, $properties); + $this->_properties = array_merge_recursive($this->_properties, $properties); } - public function resolveAllOf($parent = null) + public function resolveAllOf() { foreach ($this->_properties as $property => $value) { if ($property === 'allOf' && !empty($value)) { - $this->_properties[$property] = $this->mergeAllAllOfsInToSingleObj($value); + $this->_properties[$property] = $this->mergeAllAllOfsInToSingleObj(); } elseif ($value instanceof SpecObjectInterface && method_exists($value, 'resolveAllOf')) { - $value->resolveAllOf($this->_properties[$property]); + $value->resolveAllOf(); } elseif (is_array($value)) { foreach ($value as $k => $item) { if ($k === 'allOf' && !empty($item)) { - $this->_properties[$property][$k] = $this->mergeAllAllOfsInToSingleObj($item); - } elseif ($item instanceof SpecObjectInterface) { - $item->resolveAllOf($this->_properties[$property][$k]); + $this->_properties[$property][$k] = $this->mergeAllAllOfsInToSingleObj(); + } elseif ($item instanceof SpecObjectInterface && method_exists($item, 'resolveAllOf')) { + $item->resolveAllOf(); } } } } } - public function mergeAllAllOfsInToSingleObj($allOfs): self + public function mergeAllAllOfsInToSingleObj(): self { $allOfs = $this->allOf; /** @var static $first */ @@ -587,10 +587,10 @@ public function resolveAllOf2() // TODO foreach ($item as $kIn => $vIn) { // TODO rename var names if (!empty($vIn->allOf)) { $tempIn = $vIn->allOf; - $this->_properties[$property][$value][$k2][$kIn] = $tempIn; + $this->_properties[$property][$k2][$kIn] = $tempIn; } } - } elseif ($item instanceof SpecObjectInterface) { + } elseif ($item instanceof SpecObjectInterface && method_exists($item, 'resolveAllOf2')) { $item->resolveAllOf2(); } } diff --git a/tests/spec/SchemaTest.php b/tests/spec/SchemaTest.php index 815e6775..ca1b5ac3 100644 --- a/tests/spec/SchemaTest.php +++ b/tests/spec/SchemaTest.php @@ -425,6 +425,7 @@ public function testPropertyNameRef() // TODO cleanup public function test165ResolveAllOf() { +// return; // $openApi->resolveReferences(new \cebe\openapi\ReferenceContext($openApi, 'file:///tmp/openapi.yaml')); $openApi = Reader::readFromYamlFile(__DIR__ . '/data/resolve_all_of.yml'); $result = $openApi->validate(); @@ -432,6 +433,9 @@ public function test165ResolveAllOf() $this->assertTrue($result); $this->assertEquals([], $openApi->getErrors()); + $this->assertFalse($openApi->components->schemas['Post']->properties['user']->{'x-faker'}); + $this->assertTrue($openApi->components->schemas['Post']->properties['user']->{'x-faker2'}); + // $this->assertTrue($openApi->components->schemas['Post']->getSerializableData()); // $openApi->components->schemas['Post']->properties['user']->allOf = ['abc' => 'def']; // $openApi->components->schemas['Post']->properties['user']->__set('allOf', ['abc' => 'def']); @@ -441,7 +445,6 @@ public function test165ResolveAllOf() // $this->assertTrue($openApi->components->schemas['Post']->getSerializableData()); $this->assertSame( json_decode(json_encode($openApi->components->schemas['Post']->getSerializableData()), true) - , [] ); } diff --git a/tests/spec/data/resolve_all_of.yml b/tests/spec/data/resolve_all_of.yml index 8c81afe7..f9690c7a 100644 --- a/tests/spec/data/resolve_all_of.yml +++ b/tests/spec/data/resolve_all_of.yml @@ -17,16 +17,16 @@ components: type: integer name: type: string -# User2: -# type: object -# required: -# - id2 -# - name -# properties: -# id2: -# type: integer -# name: -# type: array + Pet: + type: object + required: + - id2 + - name2 + properties: + id2: + type: integer + name2: + type: array Post: type: object properties: @@ -37,7 +37,7 @@ components: user: allOf: - $ref: '#/components/schemas/User' -# - $ref: '#/components/schemas/User2' + - $ref: '#/components/schemas/Pet' - x-faker: false - x-faker2: true From f5a6472d3eeae7a51aa5a212032d3303b27beef8 Mon Sep 17 00:00:00 2001 From: Sohel Ahmed Mesaniya Date: Sun, 22 Sep 2024 15:32:33 +0530 Subject: [PATCH 08/24] Fix bug related to improper array merging --- src/SpecBaseObject.php | 11 +++++++- src/spec/OpenApi.php | 43 ++++++++++++++++++++++++++++++ tests/spec/data/resolve_all_of.yml | 16 ++++++++++- 3 files changed, 68 insertions(+), 2 deletions(-) diff --git a/src/SpecBaseObject.php b/src/SpecBaseObject.php index ed2476d0..7cdb6794 100644 --- a/src/SpecBaseObject.php +++ b/src/SpecBaseObject.php @@ -11,6 +11,7 @@ use cebe\openapi\exceptions\UnknownPropertyException; use cebe\openapi\json\JsonPointer; use cebe\openapi\json\JsonReference; +use cebe\openapi\spec\OpenApi; use cebe\openapi\spec\Reference; use cebe\openapi\spec\Schema; use cebe\openapi\spec\Type; @@ -534,11 +535,18 @@ public function getProperties(): array public function mergeProperties($properties) { - $this->_properties = array_merge_recursive($this->_properties, $properties); + $this->_properties = OpenApi::array_merge_recursive_distinct($this->_properties, $properties); } + private $_recursingAllOf = false; public function resolveAllOf() { + // avoid recursion to get stuck in a loop + if ($this->_recursingAllOf) { + return; + } + $this->_recursingAllOf = true; + foreach ($this->_properties as $property => $value) { if ($property === 'allOf' && !empty($value)) { $this->_properties[$property] = $this->mergeAllAllOfsInToSingleObj(); @@ -554,6 +562,7 @@ public function resolveAllOf() } } } + $this->_recursingAllOf = false; } public function mergeAllAllOfsInToSingleObj(): self diff --git a/src/spec/OpenApi.php b/src/spec/OpenApi.php index 29d38b38..b99cdc80 100644 --- a/src/spec/OpenApi.php +++ b/src/spec/OpenApi.php @@ -79,4 +79,47 @@ public function performValidation() $this->addError('Unsupported openapi version: ' . $this->openapi); } } + + /** + * Thanks https://www.php.net/manual/en/function.array-merge-recursive.php#96201 + * + * Merges any number of arrays / parameters recursively, replacing + * entries with string keys with values from latter arrays. + * If the entry or the next value to be assigned is an array, then it + * automagically treats both arguments as an array. + * Numeric entries are appended, not replaced, but only if they are + * unique + * + * Function call example: `$result = array_merge_recursive_distinct(a1, a2, ... aN);` + * TODO add test and more docs + */ + public static function array_merge_recursive_distinct() + { + $arrays = func_get_args(); + $base = array_shift($arrays); + if(!is_array($base)) { + $base = empty($base) ? [] : [$base]; + } + foreach($arrays as $append) { + if(!is_array($append)) { + $append = [$append]; + } + foreach($append as $key => $value) { + if(!array_key_exists($key, $base) and !is_numeric($key)) { + $base[$key] = $append[$key]; + continue; + } + if(is_array($value) or is_array($base[$key])) { + $base[$key] = static::array_merge_recursive_distinct($base[$key], $append[$key]); + } elseif(is_numeric($key)) { + if(!in_array($value, $base)) { + $base[] = $value; + } + } else { + $base[$key] = $value; + } + } + } + return $base; + } } diff --git a/tests/spec/data/resolve_all_of.yml b/tests/spec/data/resolve_all_of.yml index f9690c7a..887e23ae 100644 --- a/tests/spec/data/resolve_all_of.yml +++ b/tests/spec/data/resolve_all_of.yml @@ -17,6 +17,7 @@ components: type: integer name: type: string + maxLength: 10 Pet: type: object required: @@ -26,7 +27,19 @@ components: id2: type: integer name2: - type: array + type: string + maxLength: 12 + Fruit: + type: object + required: + - id3 + - name + properties: + id3: + type: integer + name: + type: string + maxLength: 14 Post: type: object properties: @@ -38,6 +51,7 @@ components: allOf: - $ref: '#/components/schemas/User' - $ref: '#/components/schemas/Pet' + - $ref: '#/components/schemas/Fruit' - x-faker: false - x-faker2: true From a9b2dbdee01d65aa29040f71078b98f324b61214 Mon Sep 17 00:00:00 2001 From: Sohel Ahmed Mesaniya Date: Mon, 23 Sep 2024 16:20:04 +0530 Subject: [PATCH 09/24] Refactor and add more tests --- src/SpecBaseObject.php | 2 +- src/spec/OpenApi.php | 6 +-- tests/spec/SchemaTest.php | 24 ++++------ tests/spec/data/resolve_all_of.yml | 6 +++ tests/spec/data/resolve_all_of_expected.php | 51 +++++++++++++++++++++ 5 files changed, 71 insertions(+), 18 deletions(-) create mode 100644 tests/spec/data/resolve_all_of_expected.php diff --git a/src/SpecBaseObject.php b/src/SpecBaseObject.php index 7cdb6794..aaaab2ee 100644 --- a/src/SpecBaseObject.php +++ b/src/SpecBaseObject.php @@ -535,7 +535,7 @@ public function getProperties(): array public function mergeProperties($properties) { - $this->_properties = OpenApi::array_merge_recursive_distinct($this->_properties, $properties); + $this->_properties = OpenApi::arrayMergeRecursiveDistinct($this->_properties, $properties); } private $_recursingAllOf = false; diff --git a/src/spec/OpenApi.php b/src/spec/OpenApi.php index b99cdc80..6a0f7513 100644 --- a/src/spec/OpenApi.php +++ b/src/spec/OpenApi.php @@ -93,7 +93,7 @@ public function performValidation() * Function call example: `$result = array_merge_recursive_distinct(a1, a2, ... aN);` * TODO add test and more docs */ - public static function array_merge_recursive_distinct() + public static function arrayMergeRecursiveDistinct() { $arrays = func_get_args(); $base = array_shift($arrays); @@ -109,8 +109,8 @@ public static function array_merge_recursive_distinct() $base[$key] = $append[$key]; continue; } - if(is_array($value) or is_array($base[$key])) { - $base[$key] = static::array_merge_recursive_distinct($base[$key], $append[$key]); + if(is_array($value) || is_array($base[$key])) { + $base[$key] = static::arrayMergeRecursiveDistinct($base[$key], $append[$key]); } elseif(is_numeric($key)) { if(!in_array($value, $base)) { $base[] = $value; diff --git a/tests/spec/SchemaTest.php b/tests/spec/SchemaTest.php index ca1b5ac3..2a394425 100644 --- a/tests/spec/SchemaTest.php +++ b/tests/spec/SchemaTest.php @@ -425,27 +425,23 @@ public function testPropertyNameRef() // TODO cleanup public function test165ResolveAllOf() { -// return; -// $openApi->resolveReferences(new \cebe\openapi\ReferenceContext($openApi, 'file:///tmp/openapi.yaml')); $openApi = Reader::readFromYamlFile(__DIR__ . '/data/resolve_all_of.yml'); $result = $openApi->validate(); -// $openApi->resolveAllOf(); $this->assertTrue($result); $this->assertEquals([], $openApi->getErrors()); $this->assertFalse($openApi->components->schemas['Post']->properties['user']->{'x-faker'}); $this->assertTrue($openApi->components->schemas['Post']->properties['user']->{'x-faker2'}); + $expected = require_once __DIR__ . '/data/resolve_all_of_expected.php'; - // $this->assertTrue($openApi->components->schemas['Post']->getSerializableData()); -// $openApi->components->schemas['Post']->properties['user']->allOf = ['abc' => 'def']; -// $openApi->components->schemas['Post']->properties['user']->__set('allOf', ['abc' => 'def']); - - // $openApi->components->schemas['Post']->resolveReferences(); -// $this->assertTrue($openApi->components->schemas['Post']); -// $this->assertTrue($openApi->components->schemas['Post']->getSerializableData()); - $this->assertSame( - json_decode(json_encode($openApi->components->schemas['Post']->getSerializableData()), true) - , [] - ); +// $this->assertSame( +// json_decode(json_encode($openApi->components->schemas['Post']->getSerializableData()), true) +// , [] +// ); + + $this->assertSame( + json_decode(json_encode($openApi->components->schemas['Post']->getSerializableData()), true) + , $expected + ); } } diff --git a/tests/spec/data/resolve_all_of.yml b/tests/spec/data/resolve_all_of.yml index 887e23ae..892dd03b 100644 --- a/tests/spec/data/resolve_all_of.yml +++ b/tests/spec/data/resolve_all_of.yml @@ -54,6 +54,12 @@ components: - $ref: '#/components/schemas/Fruit' - x-faker: false - x-faker2: true + - type: object + properties: + id4: + type: integer + name4: + type: string paths: '/': diff --git a/tests/spec/data/resolve_all_of_expected.php b/tests/spec/data/resolve_all_of_expected.php new file mode 100644 index 00000000..728a39b4 --- /dev/null +++ b/tests/spec/data/resolve_all_of_expected.php @@ -0,0 +1,51 @@ + 'object', + 'properties' => [ + 'id' => [ + 'type' => 'integer' + ], + 'content' => [ + 'type' => 'string' + ], + 'user' => [ + 'required' => [ + 'id', + 'name', + 'id2', + 'name2', + 'id3' + ], + 'type' => 'object', + 'properties' => [ + 'id' => [ + 'type' => 'integer' + ], + 'name' => [ + 'maxLength' => 14, + 'type' => 'string' + ], + 'id2' => [ + 'type' => 'integer' + ], + 'name2' => [ + 'maxLength' => 12, + 'type' => 'string' + ], + 'id3' => [ + 'type' => 'integer' + ], + 'id4' => [ + 'type' => 'integer' + ], + 'name4' => [ + 'type' => 'string' + ] + + ], + 'x-faker' => false, + 'x-faker2' => true + ], + ], +]; From b5c87a4b1526b13cd01b86f79c4611b72e4aa3c0 Mon Sep 17 00:00:00 2001 From: Sohel Ahmed Mesaniya Date: Mon, 23 Sep 2024 16:56:03 +0530 Subject: [PATCH 10/24] Refactor --- src/Reader.php | 8 +++-- src/SpecBaseObject.php | 74 +++++++++++++++++++++------------------ tests/spec/SchemaTest.php | 5 +-- 3 files changed, 47 insertions(+), 40 deletions(-) diff --git a/src/Reader.php b/src/Reader.php index 5687699d..050e95ae 100644 --- a/src/Reader.php +++ b/src/Reader.php @@ -121,13 +121,14 @@ public static function readFromJsonFile(string $fileName, string $baseType = Ope * Since version 1.5.0 this can be a string indicating the reference resolving mode: * - `inline` only resolve references to external files. * - `all` resolve all references except recursive references. + * @param bool $resolveAllOfs whether to automatically resolve all `allOf`s automatically. It will only work if [[$resolveReferences]] is `true` or [[ReferenceContext::RESOLVE_MODE_ALL]] * @return SpecObjectInterface|OpenApi the OpenApi object instance. * The type of the returned object depends on the `$baseType` argument. * @throws TypeErrorException in case invalid spec data is supplied. * @throws UnresolvableReferenceException in case references could not be resolved. * @throws IOException when the file is not readable. */ - public static function readFromYamlFile(string $fileName, string $baseType = OpenApi::class, $resolveReferences = true): SpecObjectInterface + public static function readFromYamlFile(string $fileName, string $baseType = OpenApi::class, $resolveReferences = true, $resolveAllOfs = false): SpecObjectInterface { $fileContent = file_get_contents($fileName); if ($fileContent === false) { @@ -147,8 +148,9 @@ public static function readFromYamlFile(string $fileName, string $baseType = Ope } $spec->resolveReferences(); } - $spec->resolveAllOf(); - $spec->resolveAllOf2(); // TODO rename + refactor if possible + if ($resolveAllOfs && ($resolveReferences === true || $resolveReferences === ReferenceContext::RESOLVE_MODE_ALL)) { + $spec->resolveAllOf(); + } return $spec; } } diff --git a/src/SpecBaseObject.php b/src/SpecBaseObject.php index aaaab2ee..15d904ab 100644 --- a/src/SpecBaseObject.php +++ b/src/SpecBaseObject.php @@ -33,6 +33,7 @@ abstract class SpecBaseObject implements SpecObjectInterface, DocumentContextInt private $_recursingReferences = false; private $_recursingReferenceContext = false; private $_recursingDocumentContext = false; + private $_recursingAllOf = false; private $_baseDocument; private $_jsonPointer; @@ -538,7 +539,6 @@ public function mergeProperties($properties) $this->_properties = OpenApi::arrayMergeRecursiveDistinct($this->_properties, $properties); } - private $_recursingAllOf = false; public function resolveAllOf() { // avoid recursion to get stuck in a loop @@ -548,24 +548,13 @@ public function resolveAllOf() $this->_recursingAllOf = true; foreach ($this->_properties as $property => $value) { - if ($property === 'allOf' && !empty($value)) { - $this->_properties[$property] = $this->mergeAllAllOfsInToSingleObj(); - } elseif ($value instanceof SpecObjectInterface && method_exists($value, 'resolveAllOf')) { - $value->resolveAllOf(); - } elseif (is_array($value)) { - foreach ($value as $k => $item) { - if ($k === 'allOf' && !empty($item)) { - $this->_properties[$property][$k] = $this->mergeAllAllOfsInToSingleObj(); - } elseif ($item instanceof SpecObjectInterface && method_exists($item, 'resolveAllOf')) { - $item->resolveAllOf(); - } - } - } + $this->handleMergingOfAllAllOfs($property, $value); + $this->removeAllOfKey($property, $value); } $this->_recursingAllOf = false; } - public function mergeAllAllOfsInToSingleObj(): self + public function mergeAllAllOfsInToSingleObject(): self { $allOfs = $this->allOf; /** @var static $first */ @@ -578,30 +567,45 @@ public function mergeAllAllOfsInToSingleObj(): self return $first; } - public function resolveAllOf2() // TODO + public function handleMergingOfAllAllOfs(string $property, $value): void { - foreach ($this->_properties as $property => $value) { - if ($property === 'properties' && !empty($value)) { - foreach ($value as $k => $v) { - if (!empty($v->allOf)) { - $temp = $v->allOf; - $this->_properties[$property][$k] = $temp; - } + if ($property === 'allOf' && !empty($value)) { + $this->_properties[$property] = $this->mergeAllAllOfsInToSingleObject(); + } elseif ($value instanceof SpecObjectInterface && method_exists($value, 'resolveAllOf')) { + $value->resolveAllOf(); + } elseif (is_array($value)) { + foreach ($value as $k => $item) { + if ($k === 'allOf' && !empty($item)) { + $this->_properties[$property][$k] = $this->mergeAllAllOfsInToSingleObject(); + } elseif ($item instanceof SpecObjectInterface && method_exists($item, 'resolveAllOf')) { + $item->resolveAllOf(); } - } elseif ($value instanceof SpecObjectInterface && method_exists($value, 'resolveAllOf2')) { - $value->resolveAllOf2(); - } elseif (is_array($value)) { - foreach ($value as $k2 => $item) { - if ($k2 === 'properties' && !empty($item)) { - foreach ($item as $kIn => $vIn) { // TODO rename var names - if (!empty($vIn->allOf)) { - $tempIn = $vIn->allOf; - $this->_properties[$property][$k2][$kIn] = $tempIn; - } + } + } + } + + public function removeAllOfKey(string $property, $value): void + { + if ($property === 'properties' && !empty($value)) { + foreach ($value as $k => $v) { + if (!empty($v->allOf)) { + $temp = $v->allOf; + $this->_properties[$property][$k] = $temp; + } + } + } elseif ($value instanceof SpecObjectInterface && method_exists($value, 'resolveAllOf2')) { + $value->resolveAllOf2(); + } elseif (is_array($value)) { + foreach ($value as $arrayValueKey => $item) { + if ($arrayValueKey === 'properties' && !empty($item)) { + foreach ($item as $itemKey => $itemValue) { + if (!empty($itemValue->allOf)) { + $tempIn = $itemValue->allOf; + $this->_properties[$property][$arrayValueKey][$itemKey] = $tempIn; } - } elseif ($item instanceof SpecObjectInterface && method_exists($item, 'resolveAllOf2')) { - $item->resolveAllOf2(); } + } elseif ($item instanceof SpecObjectInterface && method_exists($item, 'resolveAllOf2')) { + $item->resolveAllOf2(); } } } diff --git a/tests/spec/SchemaTest.php b/tests/spec/SchemaTest.php index 2a394425..a99f23b1 100644 --- a/tests/spec/SchemaTest.php +++ b/tests/spec/SchemaTest.php @@ -3,6 +3,7 @@ use cebe\openapi\Reader; use cebe\openapi\ReferenceContext; use cebe\openapi\spec\Discriminator; +use cebe\openapi\spec\OpenApi; use cebe\openapi\spec\Reference; use cebe\openapi\spec\Schema; use cebe\openapi\spec\Type; @@ -425,7 +426,7 @@ public function testPropertyNameRef() // TODO cleanup public function test165ResolveAllOf() { - $openApi = Reader::readFromYamlFile(__DIR__ . '/data/resolve_all_of.yml'); + $openApi = Reader::readFromYamlFile(__DIR__ . '/data/resolve_all_of.yml', OpenApi::class, true, true); $result = $openApi->validate(); $this->assertTrue($result); $this->assertEquals([], $openApi->getErrors()); @@ -437,7 +438,7 @@ public function test165ResolveAllOf() // $this->assertSame( // json_decode(json_encode($openApi->components->schemas['Post']->getSerializableData()), true) // , [] -// ); +// ); $this->assertSame( json_decode(json_encode($openApi->components->schemas['Post']->getSerializableData()), true) From fc75f0bb558061133497b84ee924a5065bf3a946 Mon Sep 17 00:00:00 2001 From: Sohel Ahmed Mesaniya Date: Mon, 23 Sep 2024 19:03:04 +0530 Subject: [PATCH 11/24] Add docs and tests --- TODO.taskpaper | 6 +- doc/array merge recursive distinct.md | 69 ++++++++++++++++++++++ src/spec/OpenApi.php | 2 +- tests/spec/OpenApiTest.php | 83 +++++++++++++++++++++++++++ tests/spec/SchemaTest.php | 1 - 5 files changed, 156 insertions(+), 5 deletions(-) create mode 100644 doc/array merge recursive distinct.md diff --git a/TODO.taskpaper b/TODO.taskpaper index 24d31516..570c4e66 100644 --- a/TODO.taskpaper +++ b/TODO.taskpaper @@ -1,8 +1,8 @@ TODO.taskpaper - ☐ create failing test - ☐ implement the solution - ☐ fix failing tests if any + ✔ create failing test @done (24-09-23 18:50) + ✔ implement the solution @done (24-09-23 18:50) + ✔ fix failing tests if any @done (24-09-23 18:50) ☐ resolve TODOs if any ☐ undo commit https://github.com/cebe/php-openapi/pull/208/commits/27e2618787ac21275cb2137e06bd35f202b51b6b ☐ undo commit https://github.com/cebe/php-openapi/pull/208/commits/b4c19c7599154029dc2035d42282b05237124d79 diff --git a/doc/array merge recursive distinct.md b/doc/array merge recursive distinct.md new file mode 100644 index 00000000..a29881e2 --- /dev/null +++ b/doc/array merge recursive distinct.md @@ -0,0 +1,69 @@ +### array merge recursive distinct + +While resolving `allOf`s (pull request https://github.com/cebe/php-openapi/pull/208), if a duplicate property is found + +```yaml +components: + schemas: + User: + type: object + required: + - id + - name # <-------------------------------------------------------------- + properties: + id: + type: integer + name: # <-------------------------------------------------------------- + type: string + maxLength: 10 # <-------------------------------------------------------------- + Pet: + type: object + required: + - id2 + - name # <-------------------------------------------------------------- + properties: + id2: + type: integer + name: # <-------------------------------------------------------------- + type: string + maxLength: 12 # <-------------------------------------------------------------- + Post: + type: object + properties: + id: + type: integer + content: + type: string + user: + allOf: + - $ref: '#/components/schemas/User' + - $ref: '#/components/schemas/Pet' + - x-faker: true +``` + +then property from the last component schema will be considered: + +```yaml +Post: + type: object + properties: + id: + type: integer + content: + type: string + user: + type: object + required: + - id + - name # <-------------------------------------------------------------- + - id2 + properties: + id: + type: integer + name: # <-------------------------------------------------------------- + type: string + maxLength: 12 # <-------------------------------------------------------------- + id2: + type: integer + x-faker: true +``` \ No newline at end of file diff --git a/src/spec/OpenApi.php b/src/spec/OpenApi.php index 6a0f7513..c444bacc 100644 --- a/src/spec/OpenApi.php +++ b/src/spec/OpenApi.php @@ -91,7 +91,7 @@ public function performValidation() * unique * * Function call example: `$result = array_merge_recursive_distinct(a1, a2, ... aN);` - * TODO add test and more docs + * More documentation is present at [array merge recursive distinct.md](../../../doc/array merge recursive distinct.md) file */ public static function arrayMergeRecursiveDistinct() { diff --git a/tests/spec/OpenApiTest.php b/tests/spec/OpenApiTest.php index 20b568ed..c6ea3edb 100644 --- a/tests/spec/OpenApiTest.php +++ b/tests/spec/OpenApiTest.php @@ -230,6 +230,89 @@ public function testSpecs($openApiFile) if ($openapi->externalDocs !== null) { $this->assertInstanceOf(\cebe\openapi\spec\ExternalDocumentation::class, $openapi->externalDocs); } + } + public function testArrayMergeRecursiveDistinct() + { + $result = OpenApi::arrayMergeRecursiveDistinct(['id', 'name'], ['id2', 'name2']); + $this->assertSame(['id', 'name', 'id2', 'name2'], $result); + + $result = OpenApi::arrayMergeRecursiveDistinct(['id', 'name'], ['id2', 'name']); + $this->assertSame(['id', 'name', 'id2'], $result); + + $result = OpenApi::arrayMergeRecursiveDistinct(['type' => 'object'], ['x-faker' => true]); + $this->assertSame(['type' => 'object', 'x-faker' => true], $result); + + $result = OpenApi::arrayMergeRecursiveDistinct([ + 'properties' => [ + 'id' => [ + 'type' => 'integer' + ], + 'name' => [ + 'type' => 'string' + ], + ] + ], [ + 'properties' => [ + 'id2' => [ + 'type' => 'integer' + ], + 'name2' => [ + 'type' => 'string' + ], + ] + ]); + $this->assertSame([ + 'properties' => [ + 'id' => [ + 'type' => 'integer' + ], + 'name' => [ + 'type' => 'string' + ], + 'id2' => [ + 'type' => 'integer' + ], + 'name2' => [ + 'type' => 'string' + ], + ] + ], $result); + + $result = OpenApi::arrayMergeRecursiveDistinct([ + 'properties' => [ + 'id' => [ + 'type' => 'integer' + ], + 'name' => [ + 'type' => 'string', + 'maxLength' => 10 + ], + ] + ], [ + 'properties' => [ + 'id2' => [ + 'type' => 'integer' + ], + 'name' => [ + 'type' => 'string', + 'maxLength' => 12 + ], + ] + ]); + $this->assertSame([ + 'properties' => [ + 'id' => [ + 'type' => 'integer' + ], + 'name' => [ + 'type' => 'string', + 'maxLength' => 12 + ], + 'id2' => [ + 'type' => 'integer' + ] + ] + ], $result); } } diff --git a/tests/spec/SchemaTest.php b/tests/spec/SchemaTest.php index a99f23b1..8184a8ec 100644 --- a/tests/spec/SchemaTest.php +++ b/tests/spec/SchemaTest.php @@ -423,7 +423,6 @@ public function testPropertyNameRef() } // https://github.com/cebe/yii2-openapi/issues/165 - // TODO cleanup public function test165ResolveAllOf() { $openApi = Reader::readFromYamlFile(__DIR__ . '/data/resolve_all_of.yml', OpenApi::class, true, true); From 980571308e98b64d3c2c901a532b0b9f012f2026 Mon Sep 17 00:00:00 2001 From: Sohel Ahmed Mesaniya Date: Mon, 23 Sep 2024 19:19:27 +0530 Subject: [PATCH 12/24] Add docs and fix bugs --- Makefile | 4 ++-- TODO.taskpaper | 3 ++- src/Reader.php | 4 ++-- src/SpecBaseObject.php | 8 ++++---- 4 files changed, 10 insertions(+), 9 deletions(-) diff --git a/Makefile b/Makefile index b55fe4e3..becbf1da 100644 --- a/Makefile +++ b/Makefile @@ -15,7 +15,7 @@ DOCKER_PHP=docker-compose run --rm php DOCKER_NODE=docker-compose run --rm -w /app node endif -CONTAINER_NAME=$$(echo $$(pwd) | tr / _ | cut -c 2-) +CONTAINER_NAME=$$(echo $$(pwd) | tr / _ | cut -c 2-) # lets have unique container name when dealing with lot of forks UID=$(shell id -u) all: @@ -36,7 +36,7 @@ check-style: php-cs-fixer.phar PHP_CS_FIXER_IGNORE_ENV=1 ./php-cs-fixer.phar fix src/ --diff --dry-run cli: - COMPOSE_PROJECT_NAME=$(CONTAINER_NAME) docker-compose exec --user=$(UID) php bash + COMPOSE_PROJECT_NAME=$(CONTAINER_NAME) docker-compose exec --user=$(UID) php bash # lets have unique container name when dealing with lot of forks cli_root: COMPOSE_PROJECT_NAME=$(CONTAINER_NAME) docker-compose exec --user="root" php bash diff --git a/TODO.taskpaper b/TODO.taskpaper index 570c4e66..9a4634b5 100644 --- a/TODO.taskpaper +++ b/TODO.taskpaper @@ -3,7 +3,8 @@ TODO.taskpaper ✔ create failing test @done (24-09-23 18:50) ✔ implement the solution @done (24-09-23 18:50) ✔ fix failing tests if any @done (24-09-23 18:50) - ☐ resolve TODOs if any + ✔ resolve TODOs if any @done (24-09-23 19:03) + ☐ readFromJsonFile ☐ undo commit https://github.com/cebe/php-openapi/pull/208/commits/27e2618787ac21275cb2137e06bd35f202b51b6b ☐ undo commit https://github.com/cebe/php-openapi/pull/208/commits/b4c19c7599154029dc2035d42282b05237124d79 ☐ review Merge Request diff --git a/src/Reader.php b/src/Reader.php index 050e95ae..a5cbd3e6 100644 --- a/src/Reader.php +++ b/src/Reader.php @@ -121,14 +121,14 @@ public static function readFromJsonFile(string $fileName, string $baseType = Ope * Since version 1.5.0 this can be a string indicating the reference resolving mode: * - `inline` only resolve references to external files. * - `all` resolve all references except recursive references. - * @param bool $resolveAllOfs whether to automatically resolve all `allOf`s automatically. It will only work if [[$resolveReferences]] is `true` or [[ReferenceContext::RESOLVE_MODE_ALL]] + * @param bool $resolveAllOfs whether to automatically resolve all `allOf`s automatically. It will only work if [[$resolveReferences]] is `true` or [[ReferenceContext::RESOLVE_MODE_ALL]]. The concept of resolving all `allOf`s is explained at https://github.com/cebe/php-openapi/pull/208 in detail with example. * @return SpecObjectInterface|OpenApi the OpenApi object instance. * The type of the returned object depends on the `$baseType` argument. * @throws TypeErrorException in case invalid spec data is supplied. * @throws UnresolvableReferenceException in case references could not be resolved. * @throws IOException when the file is not readable. */ - public static function readFromYamlFile(string $fileName, string $baseType = OpenApi::class, $resolveReferences = true, $resolveAllOfs = false): SpecObjectInterface + public static function readFromYamlFile(string $fileName, string $baseType = OpenApi::class, $resolveReferences = true, bool $resolveAllOfs = false): SpecObjectInterface { $fileContent = file_get_contents($fileName); if ($fileContent === false) { diff --git a/src/SpecBaseObject.php b/src/SpecBaseObject.php index 15d904ab..67e870bd 100644 --- a/src/SpecBaseObject.php +++ b/src/SpecBaseObject.php @@ -593,8 +593,8 @@ public function removeAllOfKey(string $property, $value): void $this->_properties[$property][$k] = $temp; } } - } elseif ($value instanceof SpecObjectInterface && method_exists($value, 'resolveAllOf2')) { - $value->resolveAllOf2(); + } elseif ($value instanceof SpecObjectInterface && method_exists($value, 'resolveAllOf')) { + $value->resolveAllOf(); } elseif (is_array($value)) { foreach ($value as $arrayValueKey => $item) { if ($arrayValueKey === 'properties' && !empty($item)) { @@ -604,8 +604,8 @@ public function removeAllOfKey(string $property, $value): void $this->_properties[$property][$arrayValueKey][$itemKey] = $tempIn; } } - } elseif ($item instanceof SpecObjectInterface && method_exists($item, 'resolveAllOf2')) { - $item->resolveAllOf2(); + } elseif ($item instanceof SpecObjectInterface && method_exists($item, 'resolveAllOf')) { + $item->resolveAllOf($arrayValueKey, $item); } } } From a3a642181a353e0c3f7b60cbb3c1a0b887db7ad7 Mon Sep 17 00:00:00 2001 From: Sohel Ahmed Mesaniya Date: Mon, 23 Sep 2024 19:40:12 +0530 Subject: [PATCH 13/24] Implement for JSON --- src/Reader.php | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/src/Reader.php b/src/Reader.php index a5cbd3e6..2568ddd0 100644 --- a/src/Reader.php +++ b/src/Reader.php @@ -74,6 +74,10 @@ public static function readFromYaml(string $yaml, string $baseType = OpenApi::cl * Since version 1.5.0 this can be a string indicating the reference resolving mode: * - `inline` only resolve references to external files. * - `all` resolve all references except recursive references. + * @param bool $resolveAllOfs whether to automatically resolve all `allOf`s automatically. It will + * only work if [[$resolveReferences]] is `true` or [[ReferenceContext::RESOLVE_MODE_ALL]]. The + * concept of resolving all `allOf`s is explained at https://github.com/cebe/php-openapi/pull/208 + * in detail with example. * @return SpecObjectInterface|OpenApi the OpenApi object instance. * The type of the returned object depends on the `$baseType` argument. * @throws TypeErrorException in case invalid spec data is supplied. @@ -81,7 +85,7 @@ public static function readFromYaml(string $yaml, string $baseType = OpenApi::cl * @throws IOException when the file is not readable. * @throws InvalidJsonPointerSyntaxException in case an invalid JSON pointer string is passed to the spec references. */ - public static function readFromJsonFile(string $fileName, string $baseType = OpenApi::class, $resolveReferences = true): SpecObjectInterface + public static function readFromJsonFile(string $fileName, string $baseType = OpenApi::class, $resolveReferences = true, bool $resolveAllOfs = false): SpecObjectInterface { $fileContent = file_get_contents($fileName); if ($fileContent === false) { @@ -101,6 +105,9 @@ public static function readFromJsonFile(string $fileName, string $baseType = Ope } $spec->resolveReferences(); } + if ($resolveAllOfs && ($resolveReferences === true || $resolveReferences === ReferenceContext::RESOLVE_MODE_ALL)) { + $spec->resolveAllOf(); + } return $spec; } From b28249f5b6cdfc22c9f66008853f2a410490cf8d Mon Sep 17 00:00:00 2001 From: Sohel Ahmed Mesaniya Date: Mon, 23 Sep 2024 19:40:29 +0530 Subject: [PATCH 14/24] Revert "Remove Composer dev dependencies apis-guru/openapi-directory, nexmo/api-specification temporarily for work-around for https://github.com/cebe/php-openapi/issues/209 and https://github.com/cebe/php-openapi/issues/210" This reverts commit 27e2618787ac21275cb2137e06bd35f202b51b6b. --- composer.json | 2 ++ 1 file changed, 2 insertions(+) diff --git a/composer.json b/composer.json index 70b9a563..46cb26f2 100644 --- a/composer.json +++ b/composer.json @@ -28,6 +28,8 @@ "phpunit/phpunit": "^6.5 || ^7.5 || ^8.5 || ^9.4", "oai/openapi-specification": "3.0.3", "mermade/openapi3-examples": "1.0.0", + "apis-guru/openapi-directory": "1.0.0", + "nexmo/api-specification": "1.0.0", "phpstan/phpstan": "^0.12.0" }, "conflict": { From 74a7e03a4e9140e2eb85d9510dbe8e34acd31979 Mon Sep 17 00:00:00 2001 From: Sohel Ahmed Mesaniya Date: Mon, 23 Sep 2024 19:40:47 +0530 Subject: [PATCH 15/24] Revert "Temporary remove old PHPUnit config" This reverts commit b4c19c7599154029dc2035d42282b05237124d79. --- phpunit.xml.dist | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/phpunit.xml.dist b/phpunit.xml.dist index 293ebd34..484250ce 100644 --- a/phpunit.xml.dist +++ b/phpunit.xml.dist @@ -13,7 +13,11 @@ ./tests - + + + ./src + + ./src From d83af169a3273bd4a79edde2aab4ad9bd2b12a00 Mon Sep 17 00:00:00 2001 From: Sohel Ahmed Mesaniya Date: Mon, 23 Sep 2024 19:58:26 +0530 Subject: [PATCH 16/24] Mange TODOs --- TODO.taskpaper | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/TODO.taskpaper b/TODO.taskpaper index 9a4634b5..f886d991 100644 --- a/TODO.taskpaper +++ b/TODO.taskpaper @@ -4,8 +4,8 @@ TODO.taskpaper ✔ implement the solution @done (24-09-23 18:50) ✔ fix failing tests if any @done (24-09-23 18:50) ✔ resolve TODOs if any @done (24-09-23 19:03) - ☐ readFromJsonFile - ☐ undo commit https://github.com/cebe/php-openapi/pull/208/commits/27e2618787ac21275cb2137e06bd35f202b51b6b - ☐ undo commit https://github.com/cebe/php-openapi/pull/208/commits/b4c19c7599154029dc2035d42282b05237124d79 - ☐ review Merge Request + ✔ readFromJsonFile @done (24-09-23 19:58) + ✔ undo commit https://github.com/cebe/php-openapi/pull/208/commits/27e2618787ac21275cb2137e06bd35f202b51b6b @done (24-09-23 19:58) + ✔ undo commit https://github.com/cebe/php-openapi/pull/208/commits/b4c19c7599154029dc2035d42282b05237124d79 @done (24-09-23 19:58) + ✔ review Merge Request @done (24-09-23 19:58) ☐ delete this file and submit Merge Request From 15faaba392c0f7adf0620be1f722488662baac9e Mon Sep 17 00:00:00 2001 From: Sohel Ahmed Mesaniya Date: Mon, 23 Sep 2024 19:59:00 +0530 Subject: [PATCH 17/24] Delete TODOs file --- TODO.taskpaper | 11 ----------- 1 file changed, 11 deletions(-) delete mode 100644 TODO.taskpaper diff --git a/TODO.taskpaper b/TODO.taskpaper deleted file mode 100644 index f886d991..00000000 --- a/TODO.taskpaper +++ /dev/null @@ -1,11 +0,0 @@ -TODO.taskpaper - - ✔ create failing test @done (24-09-23 18:50) - ✔ implement the solution @done (24-09-23 18:50) - ✔ fix failing tests if any @done (24-09-23 18:50) - ✔ resolve TODOs if any @done (24-09-23 19:03) - ✔ readFromJsonFile @done (24-09-23 19:58) - ✔ undo commit https://github.com/cebe/php-openapi/pull/208/commits/27e2618787ac21275cb2137e06bd35f202b51b6b @done (24-09-23 19:58) - ✔ undo commit https://github.com/cebe/php-openapi/pull/208/commits/b4c19c7599154029dc2035d42282b05237124d79 @done (24-09-23 19:58) - ✔ review Merge Request @done (24-09-23 19:58) - ☐ delete this file and submit Merge Request From b68d0d12f670284c02ad2700cec118258c038544 Mon Sep 17 00:00:00 2001 From: Sohel Ahmed Mesaniya Date: Mon, 23 Sep 2024 20:35:26 +0530 Subject: [PATCH 18/24] Add a test for `description` for https://github.com/php-openapi/yii2-openapi/issues/51 --- tests/spec/data/resolve_all_of.yml | 2 ++ tests/spec/data/resolve_all_of_expected.php | 3 ++- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/tests/spec/data/resolve_all_of.yml b/tests/spec/data/resolve_all_of.yml index 892dd03b..a8e58fa9 100644 --- a/tests/spec/data/resolve_all_of.yml +++ b/tests/spec/data/resolve_all_of.yml @@ -31,6 +31,7 @@ components: maxLength: 12 Fruit: type: object + description: The Fruit description required: - id3 - name @@ -60,6 +61,7 @@ components: type: integer name4: type: string + - description: The last user description paths: '/': diff --git a/tests/spec/data/resolve_all_of_expected.php b/tests/spec/data/resolve_all_of_expected.php index 728a39b4..02cef27b 100644 --- a/tests/spec/data/resolve_all_of_expected.php +++ b/tests/spec/data/resolve_all_of_expected.php @@ -44,8 +44,9 @@ ] ], + 'description' => 'The last user description', 'x-faker' => false, - 'x-faker2' => true + 'x-faker2' => true, ], ], ]; From 8698470c13efd24419cfd7342d1e91e63b968f6f Mon Sep 17 00:00:00 2001 From: Sohel Ahmed Mesaniya Date: Thu, 26 Sep 2024 12:25:29 +0530 Subject: [PATCH 19/24] Add more tests --- tests/spec/SchemaTest.php | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/tests/spec/SchemaTest.php b/tests/spec/SchemaTest.php index 8184a8ec..18fa3b69 100644 --- a/tests/spec/SchemaTest.php +++ b/tests/spec/SchemaTest.php @@ -1,6 +1,6 @@ assertInstanceOf(SpecBaseObject::class, $unresolvedAllOfOpenApi->components->schemas['Post']->properties['user']); + $this->assertIsArray($unresolvedAllOfOpenApi->components->schemas['Post']->properties['user']->allOf); + $this->assertNotEmpty($unresolvedAllOfOpenApi->components->schemas['Post']->properties['user']->allOf); + $openApi = Reader::readFromYamlFile(__DIR__ . '/data/resolve_all_of.yml', OpenApi::class, true, true); $result = $openApi->validate(); $this->assertTrue($result); $this->assertEquals([], $openApi->getErrors()); + $this->assertInstanceOf(SpecBaseObject::class, $openApi->components->schemas['Post']->properties['user']); + $this->assertObjectNotHasProperty('allOf', $openApi->components->schemas['Post']->properties['user']); + $this->assertFalse($openApi->components->schemas['Post']->properties['user']->{'x-faker'}); $this->assertTrue($openApi->components->schemas['Post']->properties['user']->{'x-faker2'}); $expected = require_once __DIR__ . '/data/resolve_all_of_expected.php'; From 81ee4edb6882943f846d95f37f3d26e42eb66662 Mon Sep 17 00:00:00 2001 From: Sohel Ahmed Mesaniya Date: Fri, 15 Nov 2024 16:52:17 +0530 Subject: [PATCH 20/24] Move `arrayMergeRecursiveDistinct()` to separate helper class --- src/Helper.php | 58 ++++++++++++++++++++++++ src/SpecBaseObject.php | 3 +- src/spec/OpenApi.php | 44 ------------------ tests/HelperTest.php | 92 ++++++++++++++++++++++++++++++++++++++ tests/spec/OpenApiTest.php | 84 +--------------------------------- 5 files changed, 152 insertions(+), 129 deletions(-) create mode 100644 src/Helper.php create mode 100644 tests/HelperTest.php diff --git a/src/Helper.php b/src/Helper.php new file mode 100644 index 00000000..9e66674e --- /dev/null +++ b/src/Helper.php @@ -0,0 +1,58 @@ + and contributors + * @license https://github.com/cebe/php-openapi/blob/master/LICENSE + */ + +namespace cebe\openapi; + +/** + * Helper class containing widely used custom functions used in library + */ +class Helper +{ + /** + * Thanks https://www.php.net/manual/en/function.array-merge-recursive.php#96201 + * + * Merges any number of arrays / parameters recursively, replacing + * entries with string keys with values from latter arrays. + * If the entry or the next value to be assigned is an array, then it + * automagically treats both arguments as an array. + * Numeric entries are appended, not replaced, but only if they are + * unique + * + * Function call example: `$result = array_merge_recursive_distinct(a1, a2, ... aN);` + * More documentation is present at [array merge recursive distinct.md](../../../doc/array merge recursive distinct.md) file + * @return array + */ + public static function arrayMergeRecursiveDistinct() + { + $arrays = func_get_args(); + $base = array_shift($arrays); + if (!is_array($base)) { + $base = empty($base) ? [] : [$base]; + } + foreach ($arrays as $append) { + if (!is_array($append)) { + $append = [$append]; + } + foreach ($append as $key => $value) { + if (!array_key_exists($key, $base) and !is_numeric($key)) { + $base[$key] = $append[$key]; + continue; + } + if (is_array($value) || is_array($base[$key])) { + $base[$key] = static::arrayMergeRecursiveDistinct($base[$key], $append[$key]); + } elseif (is_numeric($key)) { + if (!in_array($value, $base)) { + $base[] = $value; + } + } else { + $base[$key] = $value; + } + } + } + return $base; + } +} diff --git a/src/SpecBaseObject.php b/src/SpecBaseObject.php index 67e870bd..8264b257 100644 --- a/src/SpecBaseObject.php +++ b/src/SpecBaseObject.php @@ -11,7 +11,6 @@ use cebe\openapi\exceptions\UnknownPropertyException; use cebe\openapi\json\JsonPointer; use cebe\openapi\json\JsonReference; -use cebe\openapi\spec\OpenApi; use cebe\openapi\spec\Reference; use cebe\openapi\spec\Schema; use cebe\openapi\spec\Type; @@ -536,7 +535,7 @@ public function getProperties(): array public function mergeProperties($properties) { - $this->_properties = OpenApi::arrayMergeRecursiveDistinct($this->_properties, $properties); + $this->_properties = Helper::arrayMergeRecursiveDistinct($this->_properties, $properties); } public function resolveAllOf() diff --git a/src/spec/OpenApi.php b/src/spec/OpenApi.php index c444bacc..d22cd579 100644 --- a/src/spec/OpenApi.php +++ b/src/spec/OpenApi.php @@ -7,7 +7,6 @@ namespace cebe\openapi\spec; -use cebe\openapi\exceptions\TypeErrorException; use cebe\openapi\SpecBaseObject; /** @@ -79,47 +78,4 @@ public function performValidation() $this->addError('Unsupported openapi version: ' . $this->openapi); } } - - /** - * Thanks https://www.php.net/manual/en/function.array-merge-recursive.php#96201 - * - * Merges any number of arrays / parameters recursively, replacing - * entries with string keys with values from latter arrays. - * If the entry or the next value to be assigned is an array, then it - * automagically treats both arguments as an array. - * Numeric entries are appended, not replaced, but only if they are - * unique - * - * Function call example: `$result = array_merge_recursive_distinct(a1, a2, ... aN);` - * More documentation is present at [array merge recursive distinct.md](../../../doc/array merge recursive distinct.md) file - */ - public static function arrayMergeRecursiveDistinct() - { - $arrays = func_get_args(); - $base = array_shift($arrays); - if(!is_array($base)) { - $base = empty($base) ? [] : [$base]; - } - foreach($arrays as $append) { - if(!is_array($append)) { - $append = [$append]; - } - foreach($append as $key => $value) { - if(!array_key_exists($key, $base) and !is_numeric($key)) { - $base[$key] = $append[$key]; - continue; - } - if(is_array($value) || is_array($base[$key])) { - $base[$key] = static::arrayMergeRecursiveDistinct($base[$key], $append[$key]); - } elseif(is_numeric($key)) { - if(!in_array($value, $base)) { - $base[] = $value; - } - } else { - $base[$key] = $value; - } - } - } - return $base; - } } diff --git a/tests/HelperTest.php b/tests/HelperTest.php new file mode 100644 index 00000000..27942238 --- /dev/null +++ b/tests/HelperTest.php @@ -0,0 +1,92 @@ +assertSame(['id', 'name', 'id2', 'name2'], $result); + + $result = Helper::arrayMergeRecursiveDistinct(['id', 'name'], ['id2', 'name']); + $this->assertSame(['id', 'name', 'id2'], $result); + + $result = Helper::arrayMergeRecursiveDistinct(['type' => 'object'], ['x-faker' => true]); + $this->assertSame(['type' => 'object', 'x-faker' => true], $result); + + $result = Helper::arrayMergeRecursiveDistinct([ + 'properties' => [ + 'id' => [ + 'type' => 'integer' + ], + 'name' => [ + 'type' => 'string' + ], + ] + ], [ + 'properties' => [ + 'id2' => [ + 'type' => 'integer' + ], + 'name2' => [ + 'type' => 'string' + ], + ] + ]); + $this->assertSame([ + 'properties' => [ + 'id' => [ + 'type' => 'integer' + ], + 'name' => [ + 'type' => 'string' + ], + 'id2' => [ + 'type' => 'integer' + ], + 'name2' => [ + 'type' => 'string' + ], + ] + ], $result); + + $result = Helper::arrayMergeRecursiveDistinct([ + 'properties' => [ + 'id' => [ + 'type' => 'integer' + ], + 'name' => [ + 'type' => 'string', + 'maxLength' => 10 + ], + ] + ], [ + 'properties' => [ + 'id2' => [ + 'type' => 'integer' + ], + 'name' => [ + 'type' => 'string', + 'maxLength' => 12 + ], + ] + ]); + $this->assertSame([ + 'properties' => [ + 'id' => [ + 'type' => 'integer' + ], + 'name' => [ + 'type' => 'string', + 'maxLength' => 12 + ], + 'id2' => [ + 'type' => 'integer' + ] + ] + ], $result); + } + +} diff --git a/tests/spec/OpenApiTest.php b/tests/spec/OpenApiTest.php index c6ea3edb..a089442f 100644 --- a/tests/spec/OpenApiTest.php +++ b/tests/spec/OpenApiTest.php @@ -232,87 +232,5 @@ public function testSpecs($openApiFile) } } - public function testArrayMergeRecursiveDistinct() - { - $result = OpenApi::arrayMergeRecursiveDistinct(['id', 'name'], ['id2', 'name2']); - $this->assertSame(['id', 'name', 'id2', 'name2'], $result); - - $result = OpenApi::arrayMergeRecursiveDistinct(['id', 'name'], ['id2', 'name']); - $this->assertSame(['id', 'name', 'id2'], $result); - - $result = OpenApi::arrayMergeRecursiveDistinct(['type' => 'object'], ['x-faker' => true]); - $this->assertSame(['type' => 'object', 'x-faker' => true], $result); - - $result = OpenApi::arrayMergeRecursiveDistinct([ - 'properties' => [ - 'id' => [ - 'type' => 'integer' - ], - 'name' => [ - 'type' => 'string' - ], - ] - ], [ - 'properties' => [ - 'id2' => [ - 'type' => 'integer' - ], - 'name2' => [ - 'type' => 'string' - ], - ] - ]); - $this->assertSame([ - 'properties' => [ - 'id' => [ - 'type' => 'integer' - ], - 'name' => [ - 'type' => 'string' - ], - 'id2' => [ - 'type' => 'integer' - ], - 'name2' => [ - 'type' => 'string' - ], - ] - ], $result); - - $result = OpenApi::arrayMergeRecursiveDistinct([ - 'properties' => [ - 'id' => [ - 'type' => 'integer' - ], - 'name' => [ - 'type' => 'string', - 'maxLength' => 10 - ], - ] - ], [ - 'properties' => [ - 'id2' => [ - 'type' => 'integer' - ], - 'name' => [ - 'type' => 'string', - 'maxLength' => 12 - ], - ] - ]); - $this->assertSame([ - 'properties' => [ - 'id' => [ - 'type' => 'integer' - ], - 'name' => [ - 'type' => 'string', - 'maxLength' => 12 - ], - 'id2' => [ - 'type' => 'integer' - ] - ] - ], $result); - } + } From 0bc15f5d1920ca21ae59851b1b54918832df0105 Mon Sep 17 00:00:00 2001 From: Sohel Ahmed Mesaniya Date: Sat, 16 Nov 2024 20:18:07 +0530 Subject: [PATCH 21/24] Add test case explaining recursion in resolving `allOf` --- src/SpecBaseObject.php | 2 +- tests/spec/SchemaTest.php | 15 +++++ .../resolve_nested_all_of_with_reference.php | 60 +++++++++++++++++++ .../resolve_nested_all_of_with_reference.yml | 59 ++++++++++++++++++ 4 files changed, 135 insertions(+), 1 deletion(-) create mode 100644 tests/spec/data/resolve_nested_all_of_with_reference.php create mode 100644 tests/spec/data/resolve_nested_all_of_with_reference.yml diff --git a/src/SpecBaseObject.php b/src/SpecBaseObject.php index 8264b257..52bc806f 100644 --- a/src/SpecBaseObject.php +++ b/src/SpecBaseObject.php @@ -604,7 +604,7 @@ public function removeAllOfKey(string $property, $value): void } } } elseif ($item instanceof SpecObjectInterface && method_exists($item, 'resolveAllOf')) { - $item->resolveAllOf($arrayValueKey, $item); + $item->resolveAllOf(); } } } diff --git a/tests/spec/SchemaTest.php b/tests/spec/SchemaTest.php index 18fa3b69..7d49b714 100644 --- a/tests/spec/SchemaTest.php +++ b/tests/spec/SchemaTest.php @@ -452,4 +452,19 @@ public function test165ResolveAllOf() , $expected ); } + + // https://github.com/cebe/yii2-openapi/issues/165 + public function test165ResolveNestedAllOfWithReference() + { + $openApi = Reader::readFromYamlFile(__DIR__ . '/data/resolve_nested_all_of_with_reference.yml', OpenApi::class, true, true); + $result = $openApi->validate(); + $this->assertTrue($result); + $this->assertEquals([], $openApi->getErrors()); + + $expected = require_once __DIR__ . '/data/resolve_nested_all_of_with_reference.php'; + $this->assertSame( + json_decode(json_encode($openApi->components->schemas['Pet']->getSerializableData()), true) + , $expected + ); + } } diff --git a/tests/spec/data/resolve_nested_all_of_with_reference.php b/tests/spec/data/resolve_nested_all_of_with_reference.php new file mode 100644 index 00000000..ea1bea2a --- /dev/null +++ b/tests/spec/data/resolve_nested_all_of_with_reference.php @@ -0,0 +1,60 @@ + [ + 'id2', + 'name', + ], + 'type' => 'object', + 'properties' => [ + 'id2' => [ + 'type' => 'integer', + ], + 'name' => [ + 'maxLength' => 12, + 'type' => 'string', + ], + 'physical' => [ + 'type' => 'object', + 'properties' => [ + 'weight' => [ + 'type' => 'integer', + ], + 'dimension' => [ + 'type' => 'object', + 'properties' => [ + 'height' => [ + 'type' => 'integer', + ], + 'length' => [ + 'type' => 'integer', + ], + 'width' => [ + 'type' => 'integer', + ], + 'miscellaneous' => [ + 'type' => 'object', + 'properties' => [ + 'owner' => [ + 'required' => [ + 0 => 'id', + 1 => 'name', + ], + 'type' => 'object', + 'properties' => [ + 'id' => [ + 'type' => 'integer', + ], + 'name' => [ + 'maxLength' => 10, + 'type' => 'string', + ], + ], + ], + ], + ], + ], + ], + ], + ], + ], +]; \ No newline at end of file diff --git a/tests/spec/data/resolve_nested_all_of_with_reference.yml b/tests/spec/data/resolve_nested_all_of_with_reference.yml new file mode 100644 index 00000000..2ca4467f --- /dev/null +++ b/tests/spec/data/resolve_nested_all_of_with_reference.yml @@ -0,0 +1,59 @@ +openapi: 3.0.3 + +info: + title: resolve_nested_all_of_with_reference + version: 1.0.0 + +components: + schemas: + User: + type: object + required: + - id + - name + properties: + id: + type: integer + name: + type: string + maxLength: 10 + + Pet: + type: object + required: + - id2 + - name + properties: + id2: + type: integer + name: + type: string + maxLength: 12 + physical: + allOf: + - type: object + properties: + weight: + type: integer + dimension: + allOf: + - type: object + properties: + height: + type: integer + length: + type: integer + width: + type: integer + miscellaneous: + type: object + properties: + owner: + $ref: '#/components/schemas/User' + +paths: + '/': + get: + responses: + '200': + description: OK From 851311822f94353d3d497f60a57d96b59d5ead2d Mon Sep 17 00:00:00 2001 From: Sohel Ahmed Mesaniya Date: Mon, 18 Nov 2024 17:54:14 +0530 Subject: [PATCH 22/24] Update src/SpecBaseObject.php. PR feedback Co-authored-by: Carsten Brandt --- src/SpecBaseObject.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/SpecBaseObject.php b/src/SpecBaseObject.php index 52bc806f..25817da5 100644 --- a/src/SpecBaseObject.php +++ b/src/SpecBaseObject.php @@ -583,7 +583,7 @@ public function handleMergingOfAllAllOfs(string $property, $value): void } } - public function removeAllOfKey(string $property, $value): void + private function removeAllOfKey(string $property, $value): void { if ($property === 'properties' && !empty($value)) { foreach ($value as $k => $v) { From a95c9d1fa710e8f9b2b60ab7fd0b96f0c6b742b8 Mon Sep 17 00:00:00 2001 From: Sohel Ahmed Mesaniya Date: Mon, 18 Nov 2024 17:54:28 +0530 Subject: [PATCH 23/24] Update src/SpecBaseObject.php. PR feedback Co-authored-by: Carsten Brandt --- src/SpecBaseObject.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/SpecBaseObject.php b/src/SpecBaseObject.php index 25817da5..99a9858c 100644 --- a/src/SpecBaseObject.php +++ b/src/SpecBaseObject.php @@ -566,7 +566,7 @@ public function mergeAllAllOfsInToSingleObject(): self return $first; } - public function handleMergingOfAllAllOfs(string $property, $value): void + private function handleMergingOfAllAllOfs(string $property, $value): void { if ($property === 'allOf' && !empty($value)) { $this->_properties[$property] = $this->mergeAllAllOfsInToSingleObject(); From 00e4669b37b25c540a6106f22ece6c795ed2703b Mon Sep 17 00:00:00 2001 From: Sohel Ahmed Mesaniya Date: Mon, 18 Nov 2024 17:54:40 +0530 Subject: [PATCH 24/24] Update src/SpecBaseObject.php. PR feedback Co-authored-by: Carsten Brandt --- src/SpecBaseObject.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/SpecBaseObject.php b/src/SpecBaseObject.php index 99a9858c..a3fadf6b 100644 --- a/src/SpecBaseObject.php +++ b/src/SpecBaseObject.php @@ -553,7 +553,7 @@ public function resolveAllOf() $this->_recursingAllOf = false; } - public function mergeAllAllOfsInToSingleObject(): self + private function mergeAllAllOfsInToSingleObject(): self { $allOfs = $this->allOf; /** @var static $first */