From d21c4692c3ab9822675d5090970776bd05e1efe0 Mon Sep 17 00:00:00 2001 From: Carsten Brandt Date: Mon, 28 Oct 2019 17:05:36 +0100 Subject: [PATCH 1/2] Fix issue with loading reference that points to a reference --- src/spec/Reference.php | 16 ++++++++----- tests/spec/ReferenceTest.php | 23 +++++++++++++++---- tests/spec/data/reference/paths/pets.json | 3 +++ tests/spec/data/reference/subdir.yaml | 3 +++ .../reference/subdir/Parameter.PetId.json | 10 ++++++++ 5 files changed, 44 insertions(+), 11 deletions(-) create mode 100644 tests/spec/data/reference/subdir/Parameter.PetId.json diff --git a/src/spec/Reference.php b/src/spec/Reference.php index 9ab2b7d..b2e30d2 100644 --- a/src/spec/Reference.php +++ b/src/spec/Reference.php @@ -192,12 +192,16 @@ public function resolve(ReferenceContext $context = null) if ($referencedData === null) { return null; } - /** @var $referencedObject SpecObjectInterface */ - $referencedObject = new $this->_to($referencedData); + + // transitive reference + if (isset($referencedData['$ref'])) { + return (new Reference($referencedData, $this->_to))->resolve(new ReferenceContext(null, $file)); + } else { + /** @var $referencedObject SpecObjectInterface */ + $referencedObject = new $this->_to($referencedData); + } if ($jsonReference->getJsonPointer()->getPointer() === '') { $newContext = new ReferenceContext($referencedObject, $file); - $newContext->throwException = $context->throwException; - $referencedObject->setReferenceContext($newContext); if ($referencedObject instanceof DocumentContextInterface) { $referencedObject->setDocumentContext($referencedObject, $jsonReference->getJsonPointer()); } @@ -206,9 +210,9 @@ public function resolve(ReferenceContext $context = null) // the whole document. We do not know the base type of the file at this point, // so base document must be null. $newContext = new ReferenceContext(null, $file); - $newContext->throwException = $context->throwException; - $referencedObject->setReferenceContext($newContext); } + $newContext->throwException = $context->throwException; + $referencedObject->setReferenceContext($newContext); return $referencedObject; } catch (NonexistentJsonPointerReferenceException $e) { diff --git a/tests/spec/ReferenceTest.php b/tests/spec/ReferenceTest.php index 5f563a4..16f3361 100644 --- a/tests/spec/ReferenceTest.php +++ b/tests/spec/ReferenceTest.php @@ -2,6 +2,7 @@ use cebe\openapi\Reader; use cebe\openapi\spec\OpenApi; +use cebe\openapi\spec\Parameter; use cebe\openapi\spec\Reference; use cebe\openapi\spec\RequestBody; use cebe\openapi\spec\Response; @@ -178,17 +179,22 @@ public function testResolveFileInSubdir() $this->assertEquals([], $openapi->getErrors()); $this->assertTrue($result); - $this->assertInstanceOf(Reference::class, $petItems = $openapi->components->schemas['Pet']); - $this->assertInstanceOf(Reference::class, $petItems = $openapi->components->schemas['Dog']); + $this->assertInstanceOf(Reference::class, $openapi->components->schemas['Pet']); + $this->assertInstanceOf(Reference::class, $openapi->components->schemas['Dog']); + $this->assertInstanceOf(Reference::class, $openapi->components->parameters['Parameter.PetId']); $openapi->resolveReferences(new \cebe\openapi\ReferenceContext($openapi, $file)); - $this->assertInstanceOf(Schema::class, $petItems = $openapi->components->schemas['Pet']); - $this->assertInstanceOf(Schema::class, $petItems = $openapi->components->schemas['Dog']); + $this->assertInstanceOf(Schema::class, $openapi->components->schemas['Pet']); + $this->assertInstanceOf(Schema::class, $openapi->components->schemas['Dog']); + $this->assertInstanceOf(Parameter::class, $openapi->components->parameters['Parameter.PetId']); $this->assertArrayHasKey('id', $openapi->components->schemas['Pet']->properties); $this->assertArrayHasKey('name', $openapi->components->schemas['Dog']->properties); + $this->assertEquals('petId', $openapi->components->parameters['Parameter.PetId']->name); + $this->assertInstanceOf(Schema::class, $openapi->components->parameters['Parameter.PetId']->schema); + $this->assertEquals('integer', $openapi->components->parameters['Parameter.PetId']->schema->type); - // second level reference inside of definitions.yaml + // second level references $this->assertArrayHasKey('food', $openapi->components->schemas['Dog']->properties); $this->assertInstanceOf(Schema::class, $openapi->components->schemas['Dog']->properties['food']); $this->assertArrayHasKey('id', $openapi->components->schemas['Dog']->properties['food']->properties); @@ -199,6 +205,13 @@ public function testResolveFileInSubdir() $responseContent = $openapi->paths->getPath('/pets')->get->responses[200]->content['application/json']; $this->assertInstanceOf(Schema::class, $responseContent->schema); $this->assertEquals('A Pet', $responseContent->schema->description); + + // third level reference back to original file + $this->assertCount(1, $parameters = $openapi->paths->getPath('/pets')->get->parameters); + $parameter = reset($parameters); + $this->assertEquals('petId', $parameter->name); + $this->assertInstanceOf(Schema::class, $parameter->schema); + $this->assertEquals('integer', $parameter->schema->type); } public function testResolveFileHttp() diff --git a/tests/spec/data/reference/paths/pets.json b/tests/spec/data/reference/paths/pets.json index 2e6bb58..21d9c34 100644 --- a/tests/spec/data/reference/paths/pets.json +++ b/tests/spec/data/reference/paths/pets.json @@ -1,5 +1,8 @@ { "get": { + "parameters": [ + {"$ref": "../subdir.yaml#/components/parameters/Parameter.PetId"} + ], "responses": { "200": { "description": "return a pet", diff --git a/tests/spec/data/reference/subdir.yaml b/tests/spec/data/reference/subdir.yaml index 3c24e69..12da957 100644 --- a/tests/spec/data/reference/subdir.yaml +++ b/tests/spec/data/reference/subdir.yaml @@ -8,6 +8,9 @@ components: $ref: 'subdir/Pet.yaml' Dog: $ref: 'subdir/Dog.yaml' + parameters: + "Parameter.PetId": + "$ref": "./subdir/Parameter.PetId.json" paths: '/pet': get: diff --git a/tests/spec/data/reference/subdir/Parameter.PetId.json b/tests/spec/data/reference/subdir/Parameter.PetId.json new file mode 100644 index 0000000..dc7bc7e --- /dev/null +++ b/tests/spec/data/reference/subdir/Parameter.PetId.json @@ -0,0 +1,10 @@ +{ + "name": "petId", + "description": "Represents the id of a pet", + "in": "path", + "required": true, + "schema": { + "type": "integer" + }, + "style": "simple" +} From cc41b57b4f2569205d781ec0b1bfc8fe96e23ccb Mon Sep 17 00:00:00 2001 From: Carsten Brandt Date: Tue, 29 Oct 2019 14:34:59 +0100 Subject: [PATCH 2/2] avoid recursion when setting document context --- src/SpecBaseObject.php | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/SpecBaseObject.php b/src/SpecBaseObject.php index de65c2c..f811067 100644 --- a/src/SpecBaseObject.php +++ b/src/SpecBaseObject.php @@ -411,6 +411,12 @@ public function setDocumentContext(SpecObjectInterface $baseDocument, JsonPointe $this->_baseDocument = $baseDocument; $this->_jsonPointer = $jsonPointer; + // avoid recursion to get stuck in a loop + if ($this->_recursing) { + return; + } + $this->_recursing = true; + foreach ($this->_properties as $property => $value) { if ($value instanceof DocumentContextInterface) { $value->setDocumentContext($baseDocument, $jsonPointer->append($property)); @@ -422,6 +428,8 @@ public function setDocumentContext(SpecObjectInterface $baseDocument, JsonPointe } } } + + $this->_recursing = false; } /**