Skip to content

Commit 1316f57

Browse files
authored
Merge pull request #41 from cebe/more-reference-issues
Fix issue with loading reference that points to a reference
2 parents 3d86fa6 + cc41b57 commit 1316f57

File tree

6 files changed

+52
-11
lines changed

6 files changed

+52
-11
lines changed

src/SpecBaseObject.php

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -411,6 +411,12 @@ public function setDocumentContext(SpecObjectInterface $baseDocument, JsonPointe
411411
$this->_baseDocument = $baseDocument;
412412
$this->_jsonPointer = $jsonPointer;
413413

414+
// avoid recursion to get stuck in a loop
415+
if ($this->_recursing) {
416+
return;
417+
}
418+
$this->_recursing = true;
419+
414420
foreach ($this->_properties as $property => $value) {
415421
if ($value instanceof DocumentContextInterface) {
416422
$value->setDocumentContext($baseDocument, $jsonPointer->append($property));
@@ -422,6 +428,8 @@ public function setDocumentContext(SpecObjectInterface $baseDocument, JsonPointe
422428
}
423429
}
424430
}
431+
432+
$this->_recursing = false;
425433
}
426434

427435
/**

src/spec/Reference.php

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -192,12 +192,16 @@ public function resolve(ReferenceContext $context = null)
192192
if ($referencedData === null) {
193193
return null;
194194
}
195-
/** @var $referencedObject SpecObjectInterface */
196-
$referencedObject = new $this->_to($referencedData);
195+
196+
// transitive reference
197+
if (isset($referencedData['$ref'])) {
198+
return (new Reference($referencedData, $this->_to))->resolve(new ReferenceContext(null, $file));
199+
} else {
200+
/** @var $referencedObject SpecObjectInterface */
201+
$referencedObject = new $this->_to($referencedData);
202+
}
197203
if ($jsonReference->getJsonPointer()->getPointer() === '') {
198204
$newContext = new ReferenceContext($referencedObject, $file);
199-
$newContext->throwException = $context->throwException;
200-
$referencedObject->setReferenceContext($newContext);
201205
if ($referencedObject instanceof DocumentContextInterface) {
202206
$referencedObject->setDocumentContext($referencedObject, $jsonReference->getJsonPointer());
203207
}
@@ -206,9 +210,9 @@ public function resolve(ReferenceContext $context = null)
206210
// the whole document. We do not know the base type of the file at this point,
207211
// so base document must be null.
208212
$newContext = new ReferenceContext(null, $file);
209-
$newContext->throwException = $context->throwException;
210-
$referencedObject->setReferenceContext($newContext);
211213
}
214+
$newContext->throwException = $context->throwException;
215+
$referencedObject->setReferenceContext($newContext);
212216

213217
return $referencedObject;
214218
} catch (NonexistentJsonPointerReferenceException $e) {

tests/spec/ReferenceTest.php

Lines changed: 18 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
use cebe\openapi\Reader;
44
use cebe\openapi\spec\OpenApi;
5+
use cebe\openapi\spec\Parameter;
56
use cebe\openapi\spec\Reference;
67
use cebe\openapi\spec\RequestBody;
78
use cebe\openapi\spec\Response;
@@ -178,17 +179,22 @@ public function testResolveFileInSubdir()
178179
$this->assertEquals([], $openapi->getErrors());
179180
$this->assertTrue($result);
180181

181-
$this->assertInstanceOf(Reference::class, $petItems = $openapi->components->schemas['Pet']);
182-
$this->assertInstanceOf(Reference::class, $petItems = $openapi->components->schemas['Dog']);
182+
$this->assertInstanceOf(Reference::class, $openapi->components->schemas['Pet']);
183+
$this->assertInstanceOf(Reference::class, $openapi->components->schemas['Dog']);
184+
$this->assertInstanceOf(Reference::class, $openapi->components->parameters['Parameter.PetId']);
183185

184186
$openapi->resolveReferences(new \cebe\openapi\ReferenceContext($openapi, $file));
185187

186-
$this->assertInstanceOf(Schema::class, $petItems = $openapi->components->schemas['Pet']);
187-
$this->assertInstanceOf(Schema::class, $petItems = $openapi->components->schemas['Dog']);
188+
$this->assertInstanceOf(Schema::class, $openapi->components->schemas['Pet']);
189+
$this->assertInstanceOf(Schema::class, $openapi->components->schemas['Dog']);
190+
$this->assertInstanceOf(Parameter::class, $openapi->components->parameters['Parameter.PetId']);
188191
$this->assertArrayHasKey('id', $openapi->components->schemas['Pet']->properties);
189192
$this->assertArrayHasKey('name', $openapi->components->schemas['Dog']->properties);
193+
$this->assertEquals('petId', $openapi->components->parameters['Parameter.PetId']->name);
194+
$this->assertInstanceOf(Schema::class, $openapi->components->parameters['Parameter.PetId']->schema);
195+
$this->assertEquals('integer', $openapi->components->parameters['Parameter.PetId']->schema->type);
190196

191-
// second level reference inside of definitions.yaml
197+
// second level references
192198
$this->assertArrayHasKey('food', $openapi->components->schemas['Dog']->properties);
193199
$this->assertInstanceOf(Schema::class, $openapi->components->schemas['Dog']->properties['food']);
194200
$this->assertArrayHasKey('id', $openapi->components->schemas['Dog']->properties['food']->properties);
@@ -199,6 +205,13 @@ public function testResolveFileInSubdir()
199205
$responseContent = $openapi->paths->getPath('/pets')->get->responses[200]->content['application/json'];
200206
$this->assertInstanceOf(Schema::class, $responseContent->schema);
201207
$this->assertEquals('A Pet', $responseContent->schema->description);
208+
209+
// third level reference back to original file
210+
$this->assertCount(1, $parameters = $openapi->paths->getPath('/pets')->get->parameters);
211+
$parameter = reset($parameters);
212+
$this->assertEquals('petId', $parameter->name);
213+
$this->assertInstanceOf(Schema::class, $parameter->schema);
214+
$this->assertEquals('integer', $parameter->schema->type);
202215
}
203216

204217
public function testResolveFileHttp()

tests/spec/data/reference/paths/pets.json

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,8 @@
11
{
22
"get": {
3+
"parameters": [
4+
{"$ref": "../subdir.yaml#/components/parameters/Parameter.PetId"}
5+
],
36
"responses": {
47
"200": {
58
"description": "return a pet",

tests/spec/data/reference/subdir.yaml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,9 @@ components:
88
$ref: 'subdir/Pet.yaml'
99
Dog:
1010
$ref: 'subdir/Dog.yaml'
11+
parameters:
12+
"Parameter.PetId":
13+
"$ref": "./subdir/Parameter.PetId.json"
1114
paths:
1215
'/pet':
1316
get:
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
{
2+
"name": "petId",
3+
"description": "Represents the id of a pet",
4+
"in": "path",
5+
"required": true,
6+
"schema": {
7+
"type": "integer"
8+
},
9+
"style": "simple"
10+
}

0 commit comments

Comments
 (0)