From fad1d52648c5ffb4f069d3190d7cb0557bbcc317 Mon Sep 17 00:00:00 2001 From: Sohel Ahmed Mesaniya Date: Wed, 23 Apr 2025 17:08:56 +0530 Subject: [PATCH 01/14] Create PR --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index ea32da82..a077b532 100644 --- a/README.md +++ b/README.md @@ -298,3 +298,4 @@ Professional support, consulting as well as software development services are av https://www.cebe.cc/en/contact Development of this library is sponsored by [cebe.:cloud: "Your Professional Deployment Platform"](https://cebe.cloud). + From 0e354ac1e904c50fa61c836bbb92a8c646b41003 Mon Sep 17 00:00:00 2001 From: Sohel Ahmed Mesaniya Date: Wed, 23 Apr 2025 17:57:13 +0530 Subject: [PATCH 02/14] Stub --- src/spec/OpenApi.php | 8 ++++++++ tests/spec/OpenApiTest.php | 29 +++++++++++++++++++++++++++++ 2 files changed, 37 insertions(+) diff --git a/src/spec/OpenApi.php b/src/spec/OpenApi.php index 29d38b38..50a5a8da 100644 --- a/src/spec/OpenApi.php +++ b/src/spec/OpenApi.php @@ -78,5 +78,13 @@ public function performValidation() if (!empty($this->openapi) && !preg_match('/^3\.0\.\d+(-rc\d)?$/i', $this->openapi)) { $this->addError('Unsupported openapi version: ' . $this->openapi); } + + $providedFields = array_keys(json_decode(json_encode($this->getSerializableData()), true)); # TODO this getSerializableData() can be eliminated if + $allowedFields = array_keys($this->attributes()); + foreach ($providedFields as $field) { + if (!in_array($field, $allowedFields)) { + $this->addError('Invalid top level fields: ' . $field); + } + } } } diff --git a/tests/spec/OpenApiTest.php b/tests/spec/OpenApiTest.php index 20b568ed..4de3f2d5 100644 --- a/tests/spec/OpenApiTest.php +++ b/tests/spec/OpenApiTest.php @@ -232,4 +232,33 @@ public function testSpecs($openApiFile) } } + + public function testInvalidTopLevelField() + { + $openapi = new OpenApi([ + 'AAcomponents' => [ + 'User' => [ + 'type' => 'object', + 'properties' => [ + 'id' => [ + 'type' => 'integer' + ], + ] + ] + ] + ]); + $isValid = $openapi->validate(); +// $this->assertTrue($openapi->validate()); + $this->assertEquals([ + 'OpenApi is missing required property: openapi', + 'OpenApi is missing required property: info', + 'OpenApi is missing required property: paths', + ], $openapi->getErrors()); + +// // check default value of servers +// // https://github.com/OAI/OpenAPI-Specification/blob/3.0.2/versions/3.0.2.md#openapiObject +// // If the servers property is not provided, or is an empty array, the default value would be a Server Object with a url value of /. +// $this->assertCount(1, $openapi->servers); +// $this->assertEquals('/', $openapi->servers[0]->url); + } } From 58827875b9d1a917b4805cdfdcfa2b0257b368bc Mon Sep 17 00:00:00 2001 From: Sohel Ahmed Mesaniya Date: Wed, 23 Apr 2025 18:13:32 +0530 Subject: [PATCH 03/14] Fix this issue and add test --- src/spec/OpenApi.php | 5 ++--- tests/spec/OpenApiTest.php | 6 +++--- 2 files changed, 5 insertions(+), 6 deletions(-) diff --git a/src/spec/OpenApi.php b/src/spec/OpenApi.php index 50a5a8da..8a949afb 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,11 +78,11 @@ public function performValidation() $this->addError('Unsupported openapi version: ' . $this->openapi); } - $providedFields = array_keys(json_decode(json_encode($this->getSerializableData()), true)); # TODO this getSerializableData() can be eliminated if + $providedFields = array_keys($this->getRawSpecData()); $allowedFields = array_keys($this->attributes()); foreach ($providedFields as $field) { if (!in_array($field, $allowedFields)) { - $this->addError('Invalid top level fields: ' . $field); + $this->addError('Invalid top level field: "' . $field . '". More information can be obtained at https://spec.openapis.org/oas/v3.0.3.html#fixed-fields'); } } } diff --git a/tests/spec/OpenApiTest.php b/tests/spec/OpenApiTest.php index 4de3f2d5..b654b87c 100644 --- a/tests/spec/OpenApiTest.php +++ b/tests/spec/OpenApiTest.php @@ -236,7 +236,7 @@ public function testSpecs($openApiFile) public function testInvalidTopLevelField() { $openapi = new OpenApi([ - 'AAcomponents' => [ + 'AAAAAcomponents' => [ 'User' => [ 'type' => 'object', 'properties' => [ @@ -247,12 +247,12 @@ public function testInvalidTopLevelField() ] ] ]); - $isValid = $openapi->validate(); -// $this->assertTrue($openapi->validate()); + $this->assertFalse($openapi->validate()); $this->assertEquals([ 'OpenApi is missing required property: openapi', 'OpenApi is missing required property: info', 'OpenApi is missing required property: paths', + 'Invalid top level field: "AAAAAcomponents". More information can be obtained at https://spec.openapis.org/oas/v3.0.3.html#fixed-fields', ], $openapi->getErrors()); // // check default value of servers From 5b0e9b2ee43cc482104dafbe38e5c7f5cc74eb01 Mon Sep 17 00:00:00 2001 From: Sohel Ahmed Mesaniya Date: Wed, 23 Apr 2025 18:14:20 +0530 Subject: [PATCH 04/14] Undo --- README.md | 1 - 1 file changed, 1 deletion(-) diff --git a/README.md b/README.md index 1c9bddea..d7cd0715 100644 --- a/README.md +++ b/README.md @@ -332,4 +332,3 @@ Professional support, consulting as well as software development services are av https://www.cebe.cc/en/contact Development of this library is sponsored by [cebe.:cloud: "Your Professional Deployment Platform"](https://cebe.cloud). - From daed418c9e4c4e6002c16ee403e40ccb66a9a164 Mon Sep 17 00:00:00 2001 From: Sohel Ahmed Mesaniya Date: Wed, 23 Apr 2025 18:17:42 +0530 Subject: [PATCH 05/14] Make comparison strict --- src/spec/OpenApi.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/spec/OpenApi.php b/src/spec/OpenApi.php index 8a949afb..d76c747d 100644 --- a/src/spec/OpenApi.php +++ b/src/spec/OpenApi.php @@ -81,7 +81,7 @@ public function performValidation() $providedFields = array_keys($this->getRawSpecData()); $allowedFields = array_keys($this->attributes()); foreach ($providedFields as $field) { - if (!in_array($field, $allowedFields)) { + if (!in_array($field, $allowedFields, true)) { $this->addError('Invalid top level field: "' . $field . '". More information can be obtained at https://spec.openapis.org/oas/v3.0.3.html#fixed-fields'); } } From f8303c5d395a789219f66e8e6886252f1376de94 Mon Sep 17 00:00:00 2001 From: Sohel Ahmed Mesaniya Date: Thu, 24 Apr 2025 15:56:23 +0530 Subject: [PATCH 06/14] Implement this issue for all child class of SpecBaseObject - WIP - Only failing test is ReaderTest::testReadYamlWithAnchors --- src/SpecBaseObject.php | 7 ++++++- src/spec/OpenApi.php | 8 +------- 2 files changed, 7 insertions(+), 8 deletions(-) diff --git a/src/SpecBaseObject.php b/src/SpecBaseObject.php index ef93401e..b9065438 100644 --- a/src/SpecBaseObject.php +++ b/src/SpecBaseObject.php @@ -234,7 +234,12 @@ public function validate(): bool } $this->_recursingValidate = true; $valid = true; - foreach ($this->_properties as $v) { + $allowedFields = array_keys($this->attributes()); + foreach ($this->_properties as $k => $v) { + if ($allowedFields && !in_array($k, $allowedFields, true) && substr($k, 0, 2) !== 'x-' ) { + $valid = false; + $this->addError('Invalid field: "' . $k . '"'); + } if ($v instanceof SpecObjectInterface) { if (!$v->validate()) { $valid = false; diff --git a/src/spec/OpenApi.php b/src/spec/OpenApi.php index d76c747d..cc059f15 100644 --- a/src/spec/OpenApi.php +++ b/src/spec/OpenApi.php @@ -78,12 +78,6 @@ public function performValidation() $this->addError('Unsupported openapi version: ' . $this->openapi); } - $providedFields = array_keys($this->getRawSpecData()); - $allowedFields = array_keys($this->attributes()); - foreach ($providedFields as $field) { - if (!in_array($field, $allowedFields, true)) { - $this->addError('Invalid top level field: "' . $field . '". More information can be obtained at https://spec.openapis.org/oas/v3.0.3.html#fixed-fields'); - } - } + } } From a669fe6e0df4a14b82f1beb7f1f2e7dc04584760 Mon Sep 17 00:00:00 2001 From: Sohel Ahmed Mesaniya Date: Thu, 8 May 2025 13:33:41 +0530 Subject: [PATCH 07/14] Fix bug and failing test --- src/SpecBaseObject.php | 2 +- tests/spec/PathTest.php | 15 +++++++++++---- 2 files changed, 12 insertions(+), 5 deletions(-) diff --git a/src/SpecBaseObject.php b/src/SpecBaseObject.php index b9065438..4a5861fd 100644 --- a/src/SpecBaseObject.php +++ b/src/SpecBaseObject.php @@ -280,7 +280,7 @@ public function getErrors(): array if (($pos = $this->getDocumentPosition()) !== null) { $errors = [ array_map(function ($e) use ($pos) { - return "[{$pos->getPointer()}] $e"; + return $pos->getPointer() ? "[{$pos->getPointer()}] $e" : $e; }, $this->_errors) ]; } else { diff --git a/tests/spec/PathTest.php b/tests/spec/PathTest.php index 2d47e3e7..1a9d4f47 100644 --- a/tests/spec/PathTest.php +++ b/tests/spec/PathTest.php @@ -141,8 +141,11 @@ public function testPathItemReference() $openapi = Reader::readFromYamlFile($file, \cebe\openapi\spec\OpenApi::class, false); $result = $openapi->validate(); - $this->assertEquals([], $openapi->getErrors(), print_r($openapi->getErrors(), true)); - $this->assertTrue($result); + $this->assertEquals([ + 'Invalid field: "X-EXTENSION"', + 'Invalid field: "xyz-extension"' + ], $openapi->getErrors(), print_r($openapi->getErrors(), true)); + $this->assertFalse($result); $this->assertInstanceOf(Paths::class, $openapi->paths); $this->assertInstanceOf(PathItem::class, $fooPath = $openapi->paths['/foo']); @@ -173,12 +176,16 @@ public function testPathItemReference() $this->assertInstanceOf(Response::class, $path200); $this->assertEquals('A bar', $path200->description); + unset($openapi); /** @var $openapi OpenApi */ $openapi = Reader::readFromYamlFile($file, \cebe\openapi\spec\OpenApi::class, true); $result = $openapi->validate(); - $this->assertEquals([], $openapi->getErrors(), print_r($openapi->getErrors(), true)); - $this->assertTrue($result); + $this->assertEquals([ + 'Invalid field: "X-EXTENSION"', + 'Invalid field: "xyz-extension"' + ], $openapi->getErrors()); + $this->assertFalse($result); $this->assertInstanceOf(Paths::class, $openapi->paths); $this->assertInstanceOf(PathItem::class, $fooPath = $openapi->paths['/foo']); From 18c2191dc8e302fa89e9993c21ffc25d5e388278 Mon Sep 17 00:00:00 2001 From: Sohel Ahmed Mesaniya Date: Thu, 8 May 2025 13:49:19 +0530 Subject: [PATCH 08/14] Fix another failing test --- tests/spec/SchemaTest.php | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/spec/SchemaTest.php b/tests/spec/SchemaTest.php index 1600b3b0..684c1d7c 100644 --- a/tests/spec/SchemaTest.php +++ b/tests/spec/SchemaTest.php @@ -172,6 +172,7 @@ public function testDiscriminator() $result = $schema->validate(); $this->assertEquals([ + 'Invalid field: "map"', 'Discriminator is missing required property: propertyName' ], $schema->getErrors()); $this->assertFalse($result); From ca411e472ad42b87924eb82e79408fa6a2a89c63 Mon Sep 17 00:00:00 2001 From: Sohel Ahmed Mesaniya Date: Thu, 8 May 2025 16:35:14 +0530 Subject: [PATCH 09/14] Fix another failing test --- tests/spec/OpenApiTest.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/spec/OpenApiTest.php b/tests/spec/OpenApiTest.php index f036552e..c656891a 100644 --- a/tests/spec/OpenApiTest.php +++ b/tests/spec/OpenApiTest.php @@ -250,10 +250,10 @@ public function testInvalidTopLevelField() ]); $this->assertFalse($openapi->validate()); $this->assertEquals([ + 'Invalid field: "AAAAAcomponents"', 'OpenApi is missing required property: openapi', 'OpenApi is missing required property: info', 'OpenApi is missing required property: paths', - 'Invalid top level field: "AAAAAcomponents". More information can be obtained at https://spec.openapis.org/oas/v3.0.3.html#fixed-fields', ], $openapi->getErrors()); // // check default value of servers From 50de7535ae9154962a68cd3449611e63711fcc5d Mon Sep 17 00:00:00 2001 From: Sohel Ahmed Mesaniya Date: Thu, 8 May 2025 17:39:18 +0530 Subject: [PATCH 10/14] Fix another failing tests --- tests/ReaderTest.php | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/tests/ReaderTest.php b/tests/ReaderTest.php index eee95951..54b215bb 100644 --- a/tests/ReaderTest.php +++ b/tests/ReaderTest.php @@ -80,8 +80,13 @@ public function testReadYamlWithAnchors() private function assertApiContent(\cebe\openapi\spec\OpenApi $openapi) { $result = $openapi->validate(); - $this->assertEquals([], $openapi->getErrors()); - $this->assertTrue($result); + $this->assertEquals([ + '[/paths/~1foo/put/responses/200] Invalid field: "schema"', + '[/paths/~1foo/put/responses/404] Invalid field: "schema"', + '[/paths/~1foo/put/responses/428] Invalid field: "schema"', + '[/paths/~1foo/put/responses/default] Invalid field: "schema"' + ], $openapi->getErrors()); + $this->assertFalse($result); $this->assertEquals("3.0.0", $openapi->openapi); From c9f0b4bb95b94083a23965469dba4c3b63fb968a Mon Sep 17 00:00:00 2001 From: Sohel Ahmed Mesaniya Date: Thu, 8 May 2025 17:41:46 +0530 Subject: [PATCH 11/14] Fix another failing tests - 2 --- tests/ReaderTest.php | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/tests/ReaderTest.php b/tests/ReaderTest.php index 54b215bb..59e75663 100644 --- a/tests/ReaderTest.php +++ b/tests/ReaderTest.php @@ -44,7 +44,12 @@ public function testReadYamlWithAnchors() $openApiFile = __DIR__ . '/spec/data/traits-mixins.yaml'; $openapi = \cebe\openapi\Reader::readFromYamlFile($openApiFile); - $this->assertApiContent($openapi); + $this->assertApiContent($openapi, [ + '[/paths/~1foo/put/responses/200] Invalid field: "schema"', + '[/paths/~1foo/put/responses/404] Invalid field: "schema"', + '[/paths/~1foo/put/responses/428] Invalid field: "schema"', + '[/paths/~1foo/put/responses/default] Invalid field: "schema"' + ]); $putOperation = $openapi->paths['/foo']->put; $this->assertEquals('create foo', $putOperation->description); @@ -77,16 +82,11 @@ public function testReadYamlWithAnchors() $this->assertEquals('uuid of the resource', $foo->properties['uuid']->description); } - private function assertApiContent(\cebe\openapi\spec\OpenApi $openapi) + private function assertApiContent(\cebe\openapi\spec\OpenApi $openapi, $expected = []) { $result = $openapi->validate(); - $this->assertEquals([ - '[/paths/~1foo/put/responses/200] Invalid field: "schema"', - '[/paths/~1foo/put/responses/404] Invalid field: "schema"', - '[/paths/~1foo/put/responses/428] Invalid field: "schema"', - '[/paths/~1foo/put/responses/default] Invalid field: "schema"' - ], $openapi->getErrors()); - $this->assertFalse($result); + $this->assertEquals($expected, $openapi->getErrors()); + $expected ? $this->assertFalse($result) : $this->assertTrue($result); $this->assertEquals("3.0.0", $openapi->openapi); From 56a48eb43725768f79a0cc12c925143d39448f10 Mon Sep 17 00:00:00 2001 From: Sohel Ahmed Mesaniya Date: Thu, 8 May 2025 17:43:51 +0530 Subject: [PATCH 12/14] Fix style --- src/SpecBaseObject.php | 2 +- src/spec/OpenApi.php | 2 -- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/src/SpecBaseObject.php b/src/SpecBaseObject.php index 4a5861fd..0c89ea69 100644 --- a/src/SpecBaseObject.php +++ b/src/SpecBaseObject.php @@ -236,7 +236,7 @@ public function validate(): bool $valid = true; $allowedFields = array_keys($this->attributes()); foreach ($this->_properties as $k => $v) { - if ($allowedFields && !in_array($k, $allowedFields, true) && substr($k, 0, 2) !== 'x-' ) { + if ($allowedFields && !in_array($k, $allowedFields, true) && substr($k, 0, 2) !== 'x-') { $valid = false; $this->addError('Invalid field: "' . $k . '"'); } diff --git a/src/spec/OpenApi.php b/src/spec/OpenApi.php index 789fb0ef..4a8aa243 100644 --- a/src/spec/OpenApi.php +++ b/src/spec/OpenApi.php @@ -77,7 +77,5 @@ public function performValidation() if (!empty($this->openapi) && !preg_match('/^3\.0\.\d+(-rc\d)?$/i', $this->openapi)) { $this->addError('Unsupported openapi version: ' . $this->openapi); } - - } } From 50e9d2c0ee2549ae9b55221febb269bfa87e5d9e Mon Sep 17 00:00:00 2001 From: Sohel Ahmed Mesaniya Date: Thu, 8 May 2025 17:57:58 +0530 Subject: [PATCH 13/14] Fix failing test in CI --- src/SpecBaseObject.php | 4 ++++ tests/spec/PathTest.php | 1 - 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/src/SpecBaseObject.php b/src/SpecBaseObject.php index 0c89ea69..0d196611 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; /** @@ -235,6 +236,9 @@ public function validate(): bool $this->_recursingValidate = true; $valid = true; $allowedFields = array_keys($this->attributes()); + if (static::class === Schema::class) { + $allowedFields[] = 'additionalProperties'; + } foreach ($this->_properties as $k => $v) { if ($allowedFields && !in_array($k, $allowedFields, true) && substr($k, 0, 2) !== 'x-') { $valid = false; diff --git a/tests/spec/PathTest.php b/tests/spec/PathTest.php index 1a9d4f47..2464fecf 100644 --- a/tests/spec/PathTest.php +++ b/tests/spec/PathTest.php @@ -176,7 +176,6 @@ public function testPathItemReference() $this->assertInstanceOf(Response::class, $path200); $this->assertEquals('A bar', $path200->description); - unset($openapi); /** @var $openapi OpenApi */ $openapi = Reader::readFromYamlFile($file, \cebe\openapi\spec\OpenApi::class, true); From 687b3609fb6e755f72754c1089d18baf5d22dbf4 Mon Sep 17 00:00:00 2001 From: Sohel Ahmed Mesaniya Date: Thu, 8 May 2025 18:11:44 +0530 Subject: [PATCH 14/14] Cleanup --- tests/spec/OpenApiTest.php | 6 ------ 1 file changed, 6 deletions(-) diff --git a/tests/spec/OpenApiTest.php b/tests/spec/OpenApiTest.php index c656891a..f7001e43 100644 --- a/tests/spec/OpenApiTest.php +++ b/tests/spec/OpenApiTest.php @@ -255,11 +255,5 @@ public function testInvalidTopLevelField() 'OpenApi is missing required property: info', 'OpenApi is missing required property: paths', ], $openapi->getErrors()); - -// // check default value of servers -// // https://github.com/OAI/OpenAPI-Specification/blob/3.0.2/versions/3.0.2.md#openapiObject -// // If the servers property is not provided, or is an empty array, the default value would be a Server Object with a url value of /. -// $this->assertCount(1, $openapi->servers); -// $this->assertEquals('/', $openapi->servers[0]->url); } }